@webiny/app-admin 6.5.0-beta.9 → 6.5.1-beta.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 (90) hide show
  1. package/base/Base/DefaultFieldRenderers.js +4 -0
  2. package/base/Base/DefaultFieldRenderers.js.map +1 -1
  3. package/base/Base/FieldRenderers/BirthdayInputRenderer.d.ts +21 -0
  4. package/base/Base/FieldRenderers/BirthdayInputRenderer.js +39 -0
  5. package/base/Base/FieldRenderers/BirthdayInputRenderer.js.map +1 -0
  6. package/base/Base/FieldRenderers/BirthdayInputRenderer.test.d.ts +1 -0
  7. package/base/Base/FieldRenderers/BirthdayInputRenderer.test.js +123 -0
  8. package/base/Base/FieldRenderers/BirthdayInputRenderer.test.js.map +1 -0
  9. package/base/Base/FieldRenderers/ObjectRenderer/ObjectFieldComponents.d.ts +2 -0
  10. package/base/Base/FieldRenderers/ObjectRenderer/ObjectFieldComponents.js +13 -5
  11. package/base/Base/FieldRenderers/ObjectRenderer/ObjectFieldComponents.js.map +1 -1
  12. package/base/Base/FieldRenderers/ObjectRenderer/ObjectRenderer.js +8 -26
  13. package/base/Base/FieldRenderers/ObjectRenderer/ObjectRenderer.js.map +1 -1
  14. package/base/Base/FieldRenderers/ObjectRenderer/resolveItemTitle.d.ts +8 -3
  15. package/base/Base/FieldRenderers/ObjectRenderer/resolveItemTitle.js +25 -17
  16. package/base/Base/FieldRenderers/ObjectRenderer/resolveItemTitle.js.map +1 -1
  17. package/base/Base/FieldRenderers/ObjectRenderer/resolveItemTitle.test.d.ts +1 -0
  18. package/base/Base/FieldRenderers/ObjectRenderer/resolveItemTitle.test.js +106 -0
  19. package/base/Base/FieldRenderers/ObjectRenderer/resolveItemTitle.test.js.map +1 -0
  20. package/base/Base/UserMenu/SwitchTenant.d.ts +20 -0
  21. package/base/Base/UserMenu/SwitchTenant.js +45 -0
  22. package/base/Base/UserMenu/SwitchTenant.js.map +1 -0
  23. package/base/Base/UserMenu.js +4 -0
  24. package/base/Base/UserMenu.js.map +1 -1
  25. package/domain/Identity.d.ts +8 -0
  26. package/domain/Identity.js +6 -0
  27. package/domain/Identity.js.map +1 -1
  28. package/exports/admin/security.d.ts +4 -1
  29. package/exports/admin/security.js +4 -1
  30. package/features/formModel/FieldBuilder.test.js +126 -1
  31. package/features/formModel/FieldBuilder.test.js.map +1 -1
  32. package/features/formModel/abstractions.d.ts +16 -0
  33. package/features/formModel/abstractions.js +2 -1
  34. package/features/formModel/abstractions.js.map +1 -1
  35. package/features/formModel/fieldTypes/DateTimeFieldType.d.ts +65 -4
  36. package/features/formModel/fieldTypes/DateTimeFieldType.js +70 -8
  37. package/features/formModel/fieldTypes/DateTimeFieldType.js.map +1 -1
  38. package/features/formModel/fieldTypes/dateLimits.d.ts +37 -0
  39. package/features/formModel/fieldTypes/dateLimits.js +64 -0
  40. package/features/formModel/fieldTypes/dateLimits.js.map +1 -0
  41. package/features/formModel/fieldTypes/dateLimits.test.d.ts +1 -0
  42. package/features/formModel/fieldTypes/dateLimits.test.js +60 -0
  43. package/features/formModel/fieldTypes/dateLimits.test.js.map +1 -0
  44. package/features/formModel/index.d.ts +2 -2
  45. package/features/formModel/index.js +1 -1
  46. package/features/formModel/renderers.d.ts +1 -0
  47. package/features/formModel/renderers.js +1 -0
  48. package/features/security/LogIn/LogInError.d.ts +15 -0
  49. package/features/security/LogIn/LogInError.js +25 -0
  50. package/features/security/LogIn/LogInError.js.map +1 -0
  51. package/features/security/LogIn/LogInGateway.d.ts +2 -2
  52. package/features/security/LogIn/LogInGateway.js +22 -5
  53. package/features/security/LogIn/LogInGateway.js.map +1 -1
  54. package/features/security/LogIn/LogInRepository.d.ts +1 -2
  55. package/features/security/LogIn/LogInRepository.js +6 -2
  56. package/features/security/LogIn/LogInRepository.js.map +1 -1
  57. package/features/security/LogIn/LogInUseCase.d.ts +7 -2
  58. package/features/security/LogIn/LogInUseCase.js +53 -3
  59. package/features/security/LogIn/LogInUseCase.js.map +1 -1
  60. package/features/security/LogIn/LogInUseCase.test.d.ts +1 -0
  61. package/features/security/LogIn/LogInUseCase.test.js +321 -0
  62. package/features/security/LogIn/LogInUseCase.test.js.map +1 -0
  63. package/features/security/LogIn/abstractions.d.ts +48 -3
  64. package/features/security/LogIn/abstractions.js.map +1 -1
  65. package/features/security/LogIn/createLoginMutation.js +40 -27
  66. package/features/security/LogIn/createLoginMutation.js.map +1 -1
  67. package/features/security/LogIn/feature.js +2 -0
  68. package/features/security/LogIn/feature.js.map +1 -1
  69. package/features/security/LogIn/index.d.ts +1 -0
  70. package/features/security/LogIn/index.js +1 -0
  71. package/features/security/LoginTenantResolver/LoginTenantResolver.d.ts +14 -0
  72. package/features/security/LoginTenantResolver/LoginTenantResolver.js +52 -0
  73. package/features/security/LoginTenantResolver/LoginTenantResolver.js.map +1 -0
  74. package/features/security/LoginTenantResolver/abstractions.d.ts +38 -0
  75. package/features/security/LoginTenantResolver/abstractions.js +5 -0
  76. package/features/security/LoginTenantResolver/abstractions.js.map +1 -0
  77. package/features/security/LoginTenantResolver/feature.d.ts +3 -0
  78. package/features/security/LoginTenantResolver/feature.js +17 -0
  79. package/features/security/LoginTenantResolver/feature.js.map +1 -0
  80. package/features/security/LoginTenantResolver/index.d.ts +2 -0
  81. package/features/security/LoginTenantResolver/index.js +2 -0
  82. package/package.json +24 -24
  83. package/presentation/security/components/LoginNoTenantAccess.d.ts +27 -0
  84. package/presentation/security/components/LoginNoTenantAccess.js +22 -0
  85. package/presentation/security/components/LoginNoTenantAccess.js.map +1 -0
  86. package/presentation/security/components/LoginTenantPicker.d.ts +30 -0
  87. package/presentation/security/components/LoginTenantPicker.js +32 -0
  88. package/presentation/security/components/LoginTenantPicker.js.map +1 -0
  89. package/presentation/security/hooks/useAuthentication.d.ts +2 -2
  90. package/presentation/security/hooks/useAuthentication.js.map +1 -1
@@ -17,6 +17,7 @@ import { RadioButtonsRenderer } from "./FieldRenderers/RadioButtonsRenderer.js";
17
17
  import { CheckboxesRenderer } from "./FieldRenderers/CheckboxesRenderer.js";
18
18
  import { DateTimeRenderer } from "./FieldRenderers/DateTimeRenderer.js";
19
19
  import { DateTimeInputsRenderer } from "./FieldRenderers/DateTimeInputsRenderer.js";
20
+ import { BirthdayInputRenderer } from "./FieldRenderers/BirthdayInputRenderer.js";
20
21
  import { HiddenRenderer } from "./FieldRenderers/HiddenRenderer.js";
21
22
  import { KeyValueTagsRenderer } from "./FieldRenderers/ObjectRenderer/KeyValueTagsRenderer.js";
22
23
  import { FilePickerRenderer } from "./FieldRenderers/FilePickerRenderer.js";
@@ -96,6 +97,9 @@ const DefaultFieldRenderers = ()=>/*#__PURE__*/ react.createElement(react.Fragme
96
97
  }), /*#__PURE__*/ react.createElement(AdminConfig.Form.FieldRenderer, {
97
98
  name: "dateTimeInputs",
98
99
  component: DateTimeInputsRenderer
100
+ }), /*#__PURE__*/ react.createElement(AdminConfig.Form.FieldRenderer, {
101
+ name: "birthdayInput",
102
+ component: BirthdayInputRenderer
99
103
  }), /*#__PURE__*/ react.createElement(AdminConfig.Form.FieldRenderer, {
100
104
  name: "hidden",
101
105
  component: HiddenRenderer
@@ -1 +1 @@
1
- {"version":3,"file":"base/Base/DefaultFieldRenderers.js","sources":["../../../src/base/Base/DefaultFieldRenderers.tsx"],"sourcesContent":["import React from \"react\";\nimport { AdminConfig } from \"~/config/AdminConfig.js\";\nimport { InputRenderer } from \"~/base/Base/FieldRenderers/InputRenderer.js\";\nimport { SelectRenderer } from \"~/base/Base/FieldRenderers/SelectRenderer.js\";\nimport { ObjectRenderer } from \"~/base/Base/FieldRenderers/ObjectRenderer/ObjectRenderer.js\";\nimport { ObjectAccordionMultipleRenderer } from \"~/base/Base/FieldRenderers/ObjectRenderer/ObjectAccordionMultipleRenderer.js\";\nimport { DynamicZoneRenderer } from \"~/base/Base/FieldRenderers/ObjectRenderer/DynamicZoneRenderer.js\";\nimport { PassthroughRenderer } from \"~/base/Base/FieldRenderers/PassthroughRenderer.js\";\nimport { TextareaRenderer } from \"~/base/Base/FieldRenderers/TextareaRenderer.js\";\nimport { SwitchRenderer } from \"~/base/Base/FieldRenderers/SwitchRenderer.js\";\nimport { NumberInputRenderer } from \"~/base/Base/FieldRenderers/NumberInputRenderer.js\";\nimport { NumberInputsRenderer } from \"~/base/Base/FieldRenderers/NumberInputsRenderer.js\";\nimport { TextInputsRenderer } from \"~/base/Base/FieldRenderers/TextInputsRenderer.js\";\nimport { TextareasRenderer } from \"~/base/Base/FieldRenderers/TextareasRenderer.js\";\nimport { TagsRenderer } from \"~/base/Base/FieldRenderers/TagsRenderer.js\";\nimport { RadioButtonsRenderer } from \"~/base/Base/FieldRenderers/RadioButtonsRenderer.js\";\nimport { CheckboxesRenderer } from \"~/base/Base/FieldRenderers/CheckboxesRenderer.js\";\nimport { DateTimeRenderer } from \"~/base/Base/FieldRenderers/DateTimeRenderer.js\";\nimport { DateTimeInputsRenderer } from \"~/base/Base/FieldRenderers/DateTimeInputsRenderer.js\";\nimport { HiddenRenderer } from \"~/base/Base/FieldRenderers/HiddenRenderer.js\";\nimport { KeyValueTagsRenderer } from \"~/base/Base/FieldRenderers/ObjectRenderer/KeyValueTagsRenderer.js\";\nimport { FilePickerRenderer } from \"~/base/Base/FieldRenderers/FilePickerRenderer.js\";\nimport { FileUrlPickerRenderer } from \"~/base/Base/FieldRenderers/FileUrlPickerRenderer.js\";\nimport { MultiFilePickerRenderer } from \"~/base/Base/FieldRenderers/MultiFilePickerRenderer.js\";\nimport { CodeEditorRenderer } from \"~/base/Base/FieldRenderers/CodeEditorRenderer.js\";\nimport { LexicalRenderer } from \"~/base/Base/FieldRenderers/LexicalRenderer.js\";\nimport { LexicalEditorsRenderer } from \"~/base/Base/FieldRenderers/LexicalEditorsRenderer.js\";\nimport { PasswordInputRenderer } from \"~/base/Base/FieldRenderers/PasswordInputRenderer.js\";\nimport { PermissionsRenderer } from \"~/base/Base/FieldRenderers/PermissionsRenderer.js\";\nimport { RolesMultiSelectRenderer } from \"~/base/Base/FieldRenderers/RolesMultiSelectRenderer.js\";\nimport { ApiKeyTokenRenderer } from \"~/base/Base/FieldRenderers/ApiKeyTokenRenderer.js\";\nimport { MultiAutoCompleteRenderer } from \"~/base/Base/FieldRenderers/MultiAutoCompleteRenderer.js\";\nimport { AutoCompleteRenderer } from \"~/base/Base/FieldRenderers/AutoCompleteRenderer.js\";\nimport { MultiSelectRenderer } from \"~/base/Base/FieldRenderers/MultiSelectRenderer.js\";\nimport { RegisterFeature } from \"~/exports/admin.js\";\nimport { MultiValueDynamicZoneFeature } from \"~/base/Base/FieldRenderers/ObjectRenderer/MultiValueDynamicZone/feature.js\";\n\nexport const DefaultFieldRenderers = () => {\n return (\n <>\n <RegisterFeature feature={MultiValueDynamicZoneFeature} />\n <AdminConfig>\n <AdminConfig.Form.FieldRenderer name={\"textInput\"} component={InputRenderer} />\n <AdminConfig.Form.FieldRenderer name={\"textarea\"} component={TextareaRenderer} />\n <AdminConfig.Form.FieldRenderer name={\"select\"} component={SelectRenderer} />\n <AdminConfig.Form.FieldRenderer\n name={\"autoComplete\"}\n component={AutoCompleteRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"multiSelect\"}\n component={MultiSelectRenderer}\n />\n {/* @deprecated \"dropdown\" is for BC only./ */}\n <AdminConfig.Form.FieldRenderer name={\"dropdown\"} component={SelectRenderer} />\n <AdminConfig.Form.FieldRenderer\n name={\"objectAccordionSingle\"}\n component={ObjectRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"objectAccordionMultiple\"}\n component={ObjectAccordionMultipleRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"dynamicZone\"}\n component={DynamicZoneRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"passthrough\"}\n component={PassthroughRenderer}\n />\n <AdminConfig.Form.FieldRenderer name={\"switch\"} component={SwitchRenderer} />\n <AdminConfig.Form.FieldRenderer\n name={\"numberInput\"}\n component={NumberInputRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"numberInputs\"}\n component={NumberInputsRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"textInputs\"}\n component={TextInputsRenderer}\n />\n <AdminConfig.Form.FieldRenderer name={\"textareas\"} component={TextareasRenderer} />\n <AdminConfig.Form.FieldRenderer name={\"tags\"} component={TagsRenderer} />\n <AdminConfig.Form.FieldRenderer\n name={\"radioButtons\"}\n component={RadioButtonsRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"checkboxes\"}\n component={CheckboxesRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"dateTimeInput\"}\n component={DateTimeRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"dateTimeInputs\"}\n component={DateTimeInputsRenderer}\n />\n <AdminConfig.Form.FieldRenderer name={\"hidden\"} component={HiddenRenderer} />\n <AdminConfig.Form.FieldRenderer\n name={\"keyValueTags\"}\n component={KeyValueTagsRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"filePicker\"}\n component={FilePickerRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"multiFilePicker\"}\n component={MultiFilePickerRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"fileUrlPicker\"}\n component={FileUrlPickerRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"codeEditor\"}\n component={CodeEditorRenderer}\n />\n <AdminConfig.Form.FieldRenderer name={\"lexical\"} component={LexicalRenderer} />\n <AdminConfig.Form.FieldRenderer\n name={\"lexicalEditors\"}\n component={LexicalEditorsRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"passwordInput\"}\n component={PasswordInputRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"permissions\"}\n component={PermissionsRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"rolesMultiSelect\"}\n component={RolesMultiSelectRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"apiKeyToken\"}\n component={ApiKeyTokenRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"multiAutoComplete\"}\n component={MultiAutoCompleteRenderer}\n />\n </AdminConfig>\n </>\n );\n};\n"],"names":["DefaultFieldRenderers","RegisterFeature","MultiValueDynamicZoneFeature","AdminConfig","InputRenderer","TextareaRenderer","SelectRenderer","AutoCompleteRenderer","MultiSelectRenderer","ObjectRenderer","ObjectAccordionMultipleRenderer","DynamicZoneRenderer","PassthroughRenderer","SwitchRenderer","NumberInputRenderer","NumberInputsRenderer","TextInputsRenderer","TextareasRenderer","TagsRenderer","RadioButtonsRenderer","CheckboxesRenderer","DateTimeRenderer","DateTimeInputsRenderer","HiddenRenderer","KeyValueTagsRenderer","FilePickerRenderer","MultiFilePickerRenderer","FileUrlPickerRenderer","CodeEditorRenderer","LexicalRenderer","LexicalEditorsRenderer","PasswordInputRenderer","PermissionsRenderer","RolesMultiSelectRenderer","ApiKeyTokenRenderer","MultiAutoCompleteRenderer"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqCO,MAAMA,wBAAwB,IAC1B,WAAP,GACI,wDACI,oBAACC,iBAAeA;QAAC,SAASC;sBAC1B,oBAACC,aAAWA,MAAAA,WAAAA,GACR,oBAACA,YAAY,IAAI,CAAC,aAAa;QAAC,MAAM;QAAa,WAAWC;sBAC9D,oBAACD,YAAY,IAAI,CAAC,aAAa;QAAC,MAAM;QAAY,WAAWE;sBAC7D,oBAACF,YAAY,IAAI,CAAC,aAAa;QAAC,MAAM;QAAU,WAAWG;sBAC3D,oBAACH,YAAY,IAAI,CAAC,aAAa;QAC3B,MAAM;QACN,WAAWI;sBAEf,oBAACJ,YAAY,IAAI,CAAC,aAAa;QAC3B,MAAM;QACN,WAAWK;sBAGf,oBAACL,YAAY,IAAI,CAAC,aAAa;QAAC,MAAM;QAAY,WAAWG;sBAC7D,oBAACH,YAAY,IAAI,CAAC,aAAa;QAC3B,MAAM;QACN,WAAWM;sBAEf,oBAACN,YAAY,IAAI,CAAC,aAAa;QAC3B,MAAM;QACN,WAAWO;sBAEf,oBAACP,YAAY,IAAI,CAAC,aAAa;QAC3B,MAAM;QACN,WAAWQ;sBAEf,oBAACR,YAAY,IAAI,CAAC,aAAa;QAC3B,MAAM;QACN,WAAWS;sBAEf,oBAACT,YAAY,IAAI,CAAC,aAAa;QAAC,MAAM;QAAU,WAAWU;sBAC3D,oBAACV,YAAY,IAAI,CAAC,aAAa;QAC3B,MAAM;QACN,WAAWW;sBAEf,oBAACX,YAAY,IAAI,CAAC,aAAa;QAC3B,MAAM;QACN,WAAWY;sBAEf,oBAACZ,YAAY,IAAI,CAAC,aAAa;QAC3B,MAAM;QACN,WAAWa;sBAEf,oBAACb,YAAY,IAAI,CAAC,aAAa;QAAC,MAAM;QAAa,WAAWc;sBAC9D,oBAACd,YAAY,IAAI,CAAC,aAAa;QAAC,MAAM;QAAQ,WAAWe;sBACzD,oBAACf,YAAY,IAAI,CAAC,aAAa;QAC3B,MAAM;QACN,WAAWgB;sBAEf,oBAAChB,YAAY,IAAI,CAAC,aAAa;QAC3B,MAAM;QACN,WAAWiB;sBAEf,oBAACjB,YAAY,IAAI,CAAC,aAAa;QAC3B,MAAM;QACN,WAAWkB;sBAEf,oBAAClB,YAAY,IAAI,CAAC,aAAa;QAC3B,MAAM;QACN,WAAWmB;sBAEf,oBAACnB,YAAY,IAAI,CAAC,aAAa;QAAC,MAAM;QAAU,WAAWoB;sBAC3D,oBAACpB,YAAY,IAAI,CAAC,aAAa;QAC3B,MAAM;QACN,WAAWqB;sBAEf,oBAACrB,YAAY,IAAI,CAAC,aAAa;QAC3B,MAAM;QACN,WAAWsB;sBAEf,oBAACtB,YAAY,IAAI,CAAC,aAAa;QAC3B,MAAM;QACN,WAAWuB;sBAEf,oBAACvB,YAAY,IAAI,CAAC,aAAa;QAC3B,MAAM;QACN,WAAWwB;sBAEf,oBAACxB,YAAY,IAAI,CAAC,aAAa;QAC3B,MAAM;QACN,WAAWyB;sBAEf,oBAACzB,YAAY,IAAI,CAAC,aAAa;QAAC,MAAM;QAAW,WAAW0B;sBAC5D,oBAAC1B,YAAY,IAAI,CAAC,aAAa;QAC3B,MAAM;QACN,WAAW2B;sBAEf,oBAAC3B,YAAY,IAAI,CAAC,aAAa;QAC3B,MAAM;QACN,WAAW4B;sBAEf,oBAAC5B,YAAY,IAAI,CAAC,aAAa;QAC3B,MAAM;QACN,WAAW6B;sBAEf,oBAAC7B,YAAY,IAAI,CAAC,aAAa;QAC3B,MAAM;QACN,WAAW8B;sBAEf,oBAAC9B,YAAY,IAAI,CAAC,aAAa;QAC3B,MAAM;QACN,WAAW+B;sBAEf,oBAAC/B,YAAY,IAAI,CAAC,aAAa;QAC3B,MAAM;QACN,WAAWgC"}
1
+ {"version":3,"file":"base/Base/DefaultFieldRenderers.js","sources":["../../../src/base/Base/DefaultFieldRenderers.tsx"],"sourcesContent":["import React from \"react\";\nimport { AdminConfig } from \"~/config/AdminConfig.js\";\nimport { InputRenderer } from \"~/base/Base/FieldRenderers/InputRenderer.js\";\nimport { SelectRenderer } from \"~/base/Base/FieldRenderers/SelectRenderer.js\";\nimport { ObjectRenderer } from \"~/base/Base/FieldRenderers/ObjectRenderer/ObjectRenderer.js\";\nimport { ObjectAccordionMultipleRenderer } from \"~/base/Base/FieldRenderers/ObjectRenderer/ObjectAccordionMultipleRenderer.js\";\nimport { DynamicZoneRenderer } from \"~/base/Base/FieldRenderers/ObjectRenderer/DynamicZoneRenderer.js\";\nimport { PassthroughRenderer } from \"~/base/Base/FieldRenderers/PassthroughRenderer.js\";\nimport { TextareaRenderer } from \"~/base/Base/FieldRenderers/TextareaRenderer.js\";\nimport { SwitchRenderer } from \"~/base/Base/FieldRenderers/SwitchRenderer.js\";\nimport { NumberInputRenderer } from \"~/base/Base/FieldRenderers/NumberInputRenderer.js\";\nimport { NumberInputsRenderer } from \"~/base/Base/FieldRenderers/NumberInputsRenderer.js\";\nimport { TextInputsRenderer } from \"~/base/Base/FieldRenderers/TextInputsRenderer.js\";\nimport { TextareasRenderer } from \"~/base/Base/FieldRenderers/TextareasRenderer.js\";\nimport { TagsRenderer } from \"~/base/Base/FieldRenderers/TagsRenderer.js\";\nimport { RadioButtonsRenderer } from \"~/base/Base/FieldRenderers/RadioButtonsRenderer.js\";\nimport { CheckboxesRenderer } from \"~/base/Base/FieldRenderers/CheckboxesRenderer.js\";\nimport { DateTimeRenderer } from \"~/base/Base/FieldRenderers/DateTimeRenderer.js\";\nimport { DateTimeInputsRenderer } from \"~/base/Base/FieldRenderers/DateTimeInputsRenderer.js\";\nimport { BirthdayInputRenderer } from \"~/base/Base/FieldRenderers/BirthdayInputRenderer.js\";\nimport { HiddenRenderer } from \"~/base/Base/FieldRenderers/HiddenRenderer.js\";\nimport { KeyValueTagsRenderer } from \"~/base/Base/FieldRenderers/ObjectRenderer/KeyValueTagsRenderer.js\";\nimport { FilePickerRenderer } from \"~/base/Base/FieldRenderers/FilePickerRenderer.js\";\nimport { FileUrlPickerRenderer } from \"~/base/Base/FieldRenderers/FileUrlPickerRenderer.js\";\nimport { MultiFilePickerRenderer } from \"~/base/Base/FieldRenderers/MultiFilePickerRenderer.js\";\nimport { CodeEditorRenderer } from \"~/base/Base/FieldRenderers/CodeEditorRenderer.js\";\nimport { LexicalRenderer } from \"~/base/Base/FieldRenderers/LexicalRenderer.js\";\nimport { LexicalEditorsRenderer } from \"~/base/Base/FieldRenderers/LexicalEditorsRenderer.js\";\nimport { PasswordInputRenderer } from \"~/base/Base/FieldRenderers/PasswordInputRenderer.js\";\nimport { PermissionsRenderer } from \"~/base/Base/FieldRenderers/PermissionsRenderer.js\";\nimport { RolesMultiSelectRenderer } from \"~/base/Base/FieldRenderers/RolesMultiSelectRenderer.js\";\nimport { ApiKeyTokenRenderer } from \"~/base/Base/FieldRenderers/ApiKeyTokenRenderer.js\";\nimport { MultiAutoCompleteRenderer } from \"~/base/Base/FieldRenderers/MultiAutoCompleteRenderer.js\";\nimport { AutoCompleteRenderer } from \"~/base/Base/FieldRenderers/AutoCompleteRenderer.js\";\nimport { MultiSelectRenderer } from \"~/base/Base/FieldRenderers/MultiSelectRenderer.js\";\nimport { RegisterFeature } from \"~/exports/admin.js\";\nimport { MultiValueDynamicZoneFeature } from \"~/base/Base/FieldRenderers/ObjectRenderer/MultiValueDynamicZone/feature.js\";\n\nexport const DefaultFieldRenderers = () => {\n return (\n <>\n <RegisterFeature feature={MultiValueDynamicZoneFeature} />\n <AdminConfig>\n <AdminConfig.Form.FieldRenderer name={\"textInput\"} component={InputRenderer} />\n <AdminConfig.Form.FieldRenderer name={\"textarea\"} component={TextareaRenderer} />\n <AdminConfig.Form.FieldRenderer name={\"select\"} component={SelectRenderer} />\n <AdminConfig.Form.FieldRenderer\n name={\"autoComplete\"}\n component={AutoCompleteRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"multiSelect\"}\n component={MultiSelectRenderer}\n />\n {/* @deprecated \"dropdown\" is for BC only./ */}\n <AdminConfig.Form.FieldRenderer name={\"dropdown\"} component={SelectRenderer} />\n <AdminConfig.Form.FieldRenderer\n name={\"objectAccordionSingle\"}\n component={ObjectRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"objectAccordionMultiple\"}\n component={ObjectAccordionMultipleRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"dynamicZone\"}\n component={DynamicZoneRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"passthrough\"}\n component={PassthroughRenderer}\n />\n <AdminConfig.Form.FieldRenderer name={\"switch\"} component={SwitchRenderer} />\n <AdminConfig.Form.FieldRenderer\n name={\"numberInput\"}\n component={NumberInputRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"numberInputs\"}\n component={NumberInputsRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"textInputs\"}\n component={TextInputsRenderer}\n />\n <AdminConfig.Form.FieldRenderer name={\"textareas\"} component={TextareasRenderer} />\n <AdminConfig.Form.FieldRenderer name={\"tags\"} component={TagsRenderer} />\n <AdminConfig.Form.FieldRenderer\n name={\"radioButtons\"}\n component={RadioButtonsRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"checkboxes\"}\n component={CheckboxesRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"dateTimeInput\"}\n component={DateTimeRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"dateTimeInputs\"}\n component={DateTimeInputsRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"birthdayInput\"}\n component={BirthdayInputRenderer}\n />\n <AdminConfig.Form.FieldRenderer name={\"hidden\"} component={HiddenRenderer} />\n <AdminConfig.Form.FieldRenderer\n name={\"keyValueTags\"}\n component={KeyValueTagsRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"filePicker\"}\n component={FilePickerRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"multiFilePicker\"}\n component={MultiFilePickerRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"fileUrlPicker\"}\n component={FileUrlPickerRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"codeEditor\"}\n component={CodeEditorRenderer}\n />\n <AdminConfig.Form.FieldRenderer name={\"lexical\"} component={LexicalRenderer} />\n <AdminConfig.Form.FieldRenderer\n name={\"lexicalEditors\"}\n component={LexicalEditorsRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"passwordInput\"}\n component={PasswordInputRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"permissions\"}\n component={PermissionsRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"rolesMultiSelect\"}\n component={RolesMultiSelectRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"apiKeyToken\"}\n component={ApiKeyTokenRenderer}\n />\n <AdminConfig.Form.FieldRenderer\n name={\"multiAutoComplete\"}\n component={MultiAutoCompleteRenderer}\n />\n </AdminConfig>\n </>\n );\n};\n"],"names":["DefaultFieldRenderers","RegisterFeature","MultiValueDynamicZoneFeature","AdminConfig","InputRenderer","TextareaRenderer","SelectRenderer","AutoCompleteRenderer","MultiSelectRenderer","ObjectRenderer","ObjectAccordionMultipleRenderer","DynamicZoneRenderer","PassthroughRenderer","SwitchRenderer","NumberInputRenderer","NumberInputsRenderer","TextInputsRenderer","TextareasRenderer","TagsRenderer","RadioButtonsRenderer","CheckboxesRenderer","DateTimeRenderer","DateTimeInputsRenderer","BirthdayInputRenderer","HiddenRenderer","KeyValueTagsRenderer","FilePickerRenderer","MultiFilePickerRenderer","FileUrlPickerRenderer","CodeEditorRenderer","LexicalRenderer","LexicalEditorsRenderer","PasswordInputRenderer","PermissionsRenderer","RolesMultiSelectRenderer","ApiKeyTokenRenderer","MultiAutoCompleteRenderer"],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import React from "react";
2
+ declare module "../../../features/formModel/abstractions.js" {
3
+ interface IFieldRendererRegistry {
4
+ /**
5
+ * Picks a day, then a month, then a year. It offers only the dates the field's
6
+ * `dateGte`/`dateLte` limits allow; without limits, years run from this year back
7
+ * 120 years.
8
+ */
9
+ birthdayInput: {
10
+ fieldType: "datetime";
11
+ settings?: undefined;
12
+ };
13
+ }
14
+ }
15
+ /**
16
+ * Renders a date-only field as a step-by-step picker: day, then month, then year.
17
+ * The value is the same `YYYY-MM-DD` string the calendar picker stores.
18
+ */
19
+ export declare const BirthdayInputRenderer: React.ComponentType<{
20
+ field: import("../../..").IFieldVM;
21
+ }>;
@@ -0,0 +1,39 @@
1
+ import react, { useMemo } from "react";
2
+ import { DatePicker } from "@webiny/admin-ui";
3
+ import { createFieldRenderer } from "../../../features/formModel/createFieldRenderer.js";
4
+ import { getDateLimits, resolveDateLimits } from "../../../features/formModel/fieldTypes/dateLimits.js";
5
+ const toLocalDate = (value)=>{
6
+ const match = value?.match(/^(\d{4})-(\d{2})-(\d{2})/);
7
+ return match ? new Date(Number(match[1]), Number(match[2]) - 1, Number(match[3])) : void 0;
8
+ };
9
+ const BirthdayInputRenderer = createFieldRenderer(({ field })=>{
10
+ const { min, max } = resolveDateLimits(getDateLimits(field.rendererSettings));
11
+ const minDate = useMemo(()=>toLocalDate(min), [
12
+ min
13
+ ]);
14
+ const maxDate = useMemo(()=>toLocalDate(max), [
15
+ max
16
+ ]);
17
+ return /*#__PURE__*/ react.createElement("div", {
18
+ className: "w-full"
19
+ }, /*#__PURE__*/ react.createElement(DatePicker, {
20
+ hint: field.help,
21
+ type: "birthday",
22
+ value: field.value ?? void 0,
23
+ onChange: (value)=>{
24
+ field.onChange(value);
25
+ field.onBlur();
26
+ },
27
+ disabled: field.disabled,
28
+ minDate: minDate,
29
+ maxDate: maxDate,
30
+ label: field.label,
31
+ description: field.description,
32
+ note: field.note,
33
+ required: field.required,
34
+ validation: field.validation
35
+ }));
36
+ });
37
+ export { BirthdayInputRenderer };
38
+
39
+ //# sourceMappingURL=BirthdayInputRenderer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base/Base/FieldRenderers/BirthdayInputRenderer.js","sources":["../../../../src/base/Base/FieldRenderers/BirthdayInputRenderer.tsx"],"sourcesContent":["import React, { useMemo } from \"react\";\nimport { DatePicker } from \"@webiny/admin-ui\";\nimport { createFieldRenderer } from \"~/features/formModel/createFieldRenderer.js\";\nimport { getDateLimits, resolveDateLimits } from \"~/features/formModel/fieldTypes/dateLimits.js\";\n\ndeclare module \"../../../features/formModel/abstractions.js\" {\n interface IFieldRendererRegistry {\n /**\n * Picks a day, then a month, then a year. It offers only the dates the field's\n * `dateGte`/`dateLte` limits allow; without limits, years run from this year back\n * 120 years.\n */\n birthdayInput: {\n fieldType: \"datetime\";\n settings?: undefined;\n };\n }\n}\n\n/** A `YYYY-MM-DD` date as local midnight, the way the picker compares dates. */\nconst toLocalDate = (value: string | undefined): Date | undefined => {\n const match = value?.match(/^(\\d{4})-(\\d{2})-(\\d{2})/);\n return match ? new Date(Number(match[1]), Number(match[2]) - 1, Number(match[3])) : undefined;\n};\n\n/**\n * Renders a date-only field as a step-by-step picker: day, then month, then year.\n * The value is the same `YYYY-MM-DD` string the calendar picker stores.\n */\nexport const BirthdayInputRenderer = createFieldRenderer<\"birthdayInput\">(({ field }) => {\n // Worked out on every render, so the limits follow the editor's local date.\n const { min, max } = resolveDateLimits(getDateLimits(field.rendererSettings));\n const minDate = useMemo(() => toLocalDate(min), [min]);\n const maxDate = useMemo(() => toLocalDate(max), [max]);\n\n return (\n <div className=\"w-full\">\n <DatePicker\n hint={field.help}\n type={\"birthday\"}\n value={(field.value as string | undefined) ?? undefined}\n onChange={value => {\n field.onChange(value);\n field.onBlur();\n }}\n disabled={field.disabled}\n minDate={minDate}\n maxDate={maxDate}\n label={field.label}\n description={field.description}\n note={field.note}\n required={field.required}\n validation={field.validation}\n />\n </div>\n );\n});\n"],"names":["toLocalDate","value","match","Date","Number","undefined","BirthdayInputRenderer","createFieldRenderer","field","min","max","resolveDateLimits","getDateLimits","minDate","useMemo","maxDate","DatePicker"],"mappings":";;;;AAoBA,MAAMA,cAAc,CAACC;IACjB,MAAMC,QAAQD,OAAO,MAAM;IAC3B,OAAOC,QAAQ,IAAIC,KAAKC,OAAOF,KAAK,CAAC,EAAE,GAAGE,OAAOF,KAAK,CAAC,EAAE,IAAI,GAAGE,OAAOF,KAAK,CAAC,EAAE,KAAKG;AACxF;AAMO,MAAMC,wBAAwBC,oBAAqC,CAAC,EAAEC,KAAK,EAAE;IAEhF,MAAM,EAAEC,GAAG,EAAEC,GAAG,EAAE,GAAGC,kBAAkBC,cAAcJ,MAAM,gBAAgB;IAC3E,MAAMK,UAAUC,QAAQ,IAAMd,YAAYS,MAAM;QAACA;KAAI;IACrD,MAAMM,UAAUD,QAAQ,IAAMd,YAAYU,MAAM;QAACA;KAAI;IAErD,OAAO,WAAP,GACI,oBAAC;QAAI,WAAU;qBACX,oBAACM,YAAUA;QACP,MAAMR,MAAM,IAAI;QAChB,MAAM;QACN,OAAQA,MAAM,KAAK,IAA2BH;QAC9C,UAAUJ,CAAAA;YACNO,MAAM,QAAQ,CAACP;YACfO,MAAM,MAAM;QAChB;QACA,UAAUA,MAAM,QAAQ;QACxB,SAASK;QACT,SAASE;QACT,OAAOP,MAAM,KAAK;QAClB,aAAaA,MAAM,WAAW;QAC9B,MAAMA,MAAM,IAAI;QAChB,UAAUA,MAAM,QAAQ;QACxB,YAAYA,MAAM,UAAU;;AAI5C"}
@@ -0,0 +1,123 @@
1
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
2
+ import react from "react";
3
+ import { cleanup, fireEvent, render, screen, within } from "@testing-library/react";
4
+ import { AdminUiProvider } from "@webiny/admin-ui";
5
+ import { DateTimeFieldBuilder } from "../../../features/formModel/fieldTypes/DateTimeFieldType.js";
6
+ import { BirthdayInputRenderer } from "./BirthdayInputRenderer.js";
7
+ vi.mock("@webiny/icons/calendar_month.svg", ()=>({
8
+ ReactComponent: ()=>null
9
+ }));
10
+ vi.mock("@webiny/icons/close.svg", ()=>({
11
+ ReactComponent: ()=>null
12
+ }));
13
+ const createAdultField = ()=>{
14
+ const config = new DateTimeFieldBuilder().dateOnly().dateLte({
15
+ expression: "today-18y"
16
+ }).renderer("birthdayInput").build("dob");
17
+ return {
18
+ name: "dob",
19
+ qualifiedName: "dob",
20
+ type: "datetime",
21
+ label: "Date of birth",
22
+ value: void 0,
23
+ validation: {
24
+ isValid: null
25
+ },
26
+ validating: false,
27
+ required: false,
28
+ visible: true,
29
+ disabled: false,
30
+ renderer: config.renderer,
31
+ rendererSettings: config.rendererSettings,
32
+ onChange: vi.fn(),
33
+ onBlur: vi.fn(),
34
+ addItem: vi.fn(),
35
+ removeItem: vi.fn(),
36
+ focusRequested: false,
37
+ clearFocusRequest: vi.fn(),
38
+ context: {}
39
+ };
40
+ };
41
+ const cell = (step, name)=>{
42
+ const panel = document.querySelector(`div[data-step="${step}"]`);
43
+ return within(panel).getByRole("button", {
44
+ name,
45
+ hidden: true
46
+ });
47
+ };
48
+ const years = ()=>{
49
+ const panel = document.querySelector('div[data-step="year"]');
50
+ return within(panel).getAllByRole("button", {
51
+ hidden: true
52
+ }).map((button)=>Number(button.textContent));
53
+ };
54
+ const renderField = (field)=>render(/*#__PURE__*/ react.createElement(AdminUiProvider, null, /*#__PURE__*/ react.createElement(BirthdayInputRenderer, {
55
+ field: field
56
+ })));
57
+ const openPicker = ()=>{
58
+ fireEvent.click(screen.getByRole("button", {
59
+ expanded: false
60
+ }));
61
+ };
62
+ describe("BirthdayInputRenderer with a relative limit", ()=>{
63
+ beforeEach(()=>{
64
+ vi.useFakeTimers({
65
+ toFake: [
66
+ "Date"
67
+ ]
68
+ });
69
+ vi.stubGlobal("ResizeObserver", class {
70
+ observe() {}
71
+ unobserve() {}
72
+ disconnect() {}
73
+ });
74
+ });
75
+ afterEach(()=>{
76
+ cleanup();
77
+ vi.useRealTimers();
78
+ vi.unstubAllGlobals();
79
+ });
80
+ it("starts the year list at the cut-off year", ()=>{
81
+ vi.setSystemTime(new Date(2026, 9, 19, 12));
82
+ renderField(createAdultField());
83
+ openPicker();
84
+ expect(years()[0]).toBe(2008);
85
+ expect(years().some((year)=>year > 2008)).toBe(false);
86
+ });
87
+ it("dims the cut-off year for a day after the cut-off", ()=>{
88
+ vi.setSystemTime(new Date(2026, 9, 19, 12));
89
+ renderField(createAdultField());
90
+ openPicker();
91
+ fireEvent.click(cell("day", "20"));
92
+ fireEvent.click(cell("month", "October"));
93
+ expect(cell("year", "2008").disabled).toBe(true);
94
+ expect(cell("year", "2007").disabled).toBe(false);
95
+ });
96
+ it("allows the cut-off day itself", ()=>{
97
+ vi.setSystemTime(new Date(2026, 9, 19, 12));
98
+ const field = createAdultField();
99
+ renderField(field);
100
+ openPicker();
101
+ fireEvent.click(cell("day", "19"));
102
+ fireEvent.click(cell("month", "October"));
103
+ expect(cell("year", "2008").disabled).toBe(false);
104
+ fireEvent.click(cell("year", "2008"));
105
+ expect(field.onChange).toHaveBeenLastCalledWith("2008-10-19");
106
+ });
107
+ it("moves the cut-off with the date", ()=>{
108
+ vi.setSystemTime(new Date(2026, 9, 20, 12));
109
+ renderField(createAdultField());
110
+ openPicker();
111
+ fireEvent.click(cell("day", "20"));
112
+ fireEvent.click(cell("month", "October"));
113
+ expect(cell("year", "2008").disabled).toBe(false);
114
+ });
115
+ it("moves the year list on 1 January", ()=>{
116
+ vi.setSystemTime(new Date(2027, 0, 1, 12));
117
+ renderField(createAdultField());
118
+ openPicker();
119
+ expect(years()[0]).toBe(2009);
120
+ });
121
+ });
122
+
123
+ //# sourceMappingURL=BirthdayInputRenderer.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base/Base/FieldRenderers/BirthdayInputRenderer.test.js","sources":["../../../../src/base/Base/FieldRenderers/BirthdayInputRenderer.test.tsx"],"sourcesContent":["import { afterEach, beforeEach, describe, it, expect, vi } from \"vitest\";\nimport React from \"react\";\nimport { cleanup, fireEvent, render, screen, within } from \"@testing-library/react\";\nimport { AdminUiProvider } from \"@webiny/admin-ui\";\nimport type { IFieldVM } from \"~/features/formModel/abstractions.js\";\nimport { DateTimeFieldBuilder } from \"~/features/formModel/fieldTypes/DateTimeFieldType.js\";\nimport { BirthdayInputRenderer } from \"./BirthdayInputRenderer.js\";\n\n// Tests don't turn SVG files into components, so the picker's icons are stubbed.\nvi.mock(\"@webiny/icons/calendar_month.svg\", () => ({ ReactComponent: () => null }));\nvi.mock(\"@webiny/icons/close.svg\", () => ({ ReactComponent: () => null }));\n\n/** A date-only field of 18 or older, as `datetime()` hands it to the renderer. */\nconst createAdultField = (): IFieldVM => {\n const config = new DateTimeFieldBuilder()\n .dateOnly()\n .dateLte({ expression: \"today-18y\" })\n .renderer(\"birthdayInput\")\n .build(\"dob\");\n\n return {\n name: \"dob\",\n qualifiedName: \"dob\",\n type: \"datetime\",\n label: \"Date of birth\",\n value: undefined,\n validation: { isValid: null },\n validating: false,\n required: false,\n visible: true,\n disabled: false,\n renderer: config.renderer,\n rendererSettings: config.rendererSettings,\n onChange: vi.fn(),\n onBlur: vi.fn(),\n addItem: vi.fn(),\n removeItem: vi.fn(),\n focusRequested: false,\n clearFocusRequest: vi.fn(),\n context: {}\n };\n};\n\n/**\n * A step's grid. The header segments carry `data-step` too, so only the grid `div` matches.\n * Only the current step's grid is visible, so the cells are looked up as hidden as well.\n */\nconst cell = (step: \"day\" | \"month\" | \"year\", name: string) => {\n const panel = document.querySelector<HTMLElement>(`div[data-step=\"${step}\"]`)!;\n return within(panel).getByRole<HTMLButtonElement>(\"button\", { name, hidden: true });\n};\n\nconst years = () => {\n const panel = document.querySelector<HTMLElement>(`div[data-step=\"year\"]`)!;\n return within(panel)\n .getAllByRole(\"button\", { hidden: true })\n .map(button => Number(button.textContent));\n};\n\nconst renderField = (field: IFieldVM) =>\n render(\n <AdminUiProvider>\n <BirthdayInputRenderer field={field} />\n </AdminUiProvider>\n );\n\nconst openPicker = () => {\n fireEvent.click(screen.getByRole(\"button\", { expanded: false }));\n};\n\n/**\n * The renderer works the limits out from the editor's local date on every render. Only `Date`\n * is frozen, so the picker's own timers and animation frames run as usual.\n */\ndescribe(\"BirthdayInputRenderer with a relative limit\", () => {\n beforeEach(() => {\n vi.useFakeTimers({ toFake: [\"Date\"] });\n // Polyfilled per test: jsdom has no ResizeObserver, which the popover and scroll area use.\n vi.stubGlobal(\n \"ResizeObserver\",\n class {\n observe() {}\n unobserve() {}\n disconnect() {}\n }\n );\n });\n\n afterEach(() => {\n cleanup();\n vi.useRealTimers();\n vi.unstubAllGlobals();\n });\n\n it(\"starts the year list at the cut-off year\", () => {\n vi.setSystemTime(new Date(2026, 9, 19, 12));\n renderField(createAdultField());\n openPicker();\n\n expect(years()[0]).toBe(2008);\n expect(years().some(year => year > 2008)).toBe(false);\n });\n\n it(\"dims the cut-off year for a day after the cut-off\", () => {\n vi.setSystemTime(new Date(2026, 9, 19, 12));\n renderField(createAdultField());\n openPicker();\n\n fireEvent.click(cell(\"day\", \"20\"));\n fireEvent.click(cell(\"month\", \"October\"));\n\n expect(cell(\"year\", \"2008\").disabled).toBe(true);\n expect(cell(\"year\", \"2007\").disabled).toBe(false);\n });\n\n it(\"allows the cut-off day itself\", () => {\n vi.setSystemTime(new Date(2026, 9, 19, 12));\n const field = createAdultField();\n renderField(field);\n openPicker();\n\n fireEvent.click(cell(\"day\", \"19\"));\n fireEvent.click(cell(\"month\", \"October\"));\n expect(cell(\"year\", \"2008\").disabled).toBe(false);\n\n fireEvent.click(cell(\"year\", \"2008\"));\n expect(field.onChange).toHaveBeenLastCalledWith(\"2008-10-19\");\n });\n\n it(\"moves the cut-off with the date\", () => {\n vi.setSystemTime(new Date(2026, 9, 20, 12));\n renderField(createAdultField());\n openPicker();\n\n fireEvent.click(cell(\"day\", \"20\"));\n fireEvent.click(cell(\"month\", \"October\"));\n\n expect(cell(\"year\", \"2008\").disabled).toBe(false);\n });\n\n it(\"moves the year list on 1 January\", () => {\n vi.setSystemTime(new Date(2027, 0, 1, 12));\n renderField(createAdultField());\n openPicker();\n\n expect(years()[0]).toBe(2009);\n });\n});\n"],"names":["vi","createAdultField","config","DateTimeFieldBuilder","undefined","cell","step","name","panel","document","within","years","button","Number","renderField","field","render","AdminUiProvider","BirthdayInputRenderer","openPicker","fireEvent","screen","describe","beforeEach","afterEach","cleanup","it","Date","expect","year"],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import React from "react";
2
2
  import type { IObjectFieldItemVM, LayoutNodeVM } from "../../../../features/formModel/index.js";
3
+ import { FieldValueFormatter } from "../../../../features/formModel/index.js";
3
4
  import type { ISortableItemProps } from "../../../../presentation/sortable/index.js";
4
5
  export declare const NestedLayout: (({ layout }: {
5
6
  layout: LayoutNodeVM[];
@@ -10,6 +11,7 @@ export declare const NestedLayout: (({ layout }: {
10
11
  layout: LayoutNodeVM[];
11
12
  }) => React.JSX.Element;
12
13
  };
14
+ export declare function useFieldValueFormatters(): FieldValueFormatter.Interface[];
13
15
  export interface ListItemRendererProps {
14
16
  item: IObjectFieldItemVM;
15
17
  index: number;
@@ -1,11 +1,12 @@
1
- import react, { useEffect, useState } from "react";
1
+ import react, { useEffect, useMemo, useState } from "react";
2
2
  import { observer } from "mobx-react-lite";
3
3
  import { Accordion, Button, IconButton } from "@webiny/admin-ui";
4
4
  import { ReactComponent } from "@webiny/icons/add.svg";
5
5
  import { ReactComponent as delete_outline_svg_ReactComponent } from "@webiny/icons/delete_outline.svg";
6
6
  import { ReactComponent as arrow_upward_svg_ReactComponent } from "@webiny/icons/arrow_upward.svg";
7
7
  import { ReactComponent as arrow_downward_svg_ReactComponent } from "@webiny/icons/arrow_downward.svg";
8
- import { hasSubtreeFocusRequest } from "../../../../features/formModel/index.js";
8
+ import { useContainer } from "@webiny/app";
9
+ import { FieldValueFormatter, hasSubtreeFocusRequest } from "../../../../features/formModel/index.js";
9
10
  import { LayoutNodeRenderer } from "../../../../features/formModel/FormView.js";
10
11
  import { resolveItemDescription, resolveItemTitle } from "./resolveItemTitle.js";
11
12
  const NestedLayout = observer(({ layout })=>/*#__PURE__*/ react.createElement("div", {
@@ -14,8 +15,15 @@ const NestedLayout = observer(({ layout })=>/*#__PURE__*/ react.createElement("d
14
15
  key: index,
15
16
  node: node
16
17
  }))));
18
+ function useFieldValueFormatters() {
19
+ const container = useContainer();
20
+ return useMemo(()=>container.resolveAll(FieldValueFormatter), [
21
+ container
22
+ ]);
23
+ }
17
24
  const ListItemRenderer = observer(({ item, index, total, label, itemTitle, itemDescription, disabled, sortable })=>{
18
25
  const [open, setOpen] = useState(false);
26
+ const formatters = useFieldValueFormatters();
19
27
  const hasFocusRequest = hasSubtreeFocusRequest(item.fields);
20
28
  useEffect(()=>{
21
29
  if (hasFocusRequest) setOpen(true);
@@ -51,8 +59,8 @@ const ListItemRenderer = observer(({ item, index, total, label, itemTitle, itemD
51
59
  variant: "container",
52
60
  openClosedIndicatorPosition: "left"
53
61
  }, /*#__PURE__*/ react.createElement(Accordion.Item, {
54
- title: resolveItemTitle(item, index, label, itemTitle),
55
- description: resolveItemDescription(item, index, itemDescription),
62
+ title: resolveItemTitle(item.fields, index, `${label || "Item"} #${index + 1}`, itemTitle, formatters),
63
+ description: resolveItemDescription(item.fields, index, itemDescription, formatters),
56
64
  actions: disabled ? null : actions,
57
65
  open: open,
58
66
  onOpenChange: setOpen,
@@ -98,6 +106,6 @@ const AddItemButton = ({ label, disabled, onAdd })=>/*#__PURE__*/ react.createEl
98
106
  onClick: onAdd,
99
107
  disabled: disabled
100
108
  }));
101
- export { AddItemButton, ListItemRenderer, NestedLayout };
109
+ export { AddItemButton, ListItemRenderer, NestedLayout, useFieldValueFormatters };
102
110
 
103
111
  //# sourceMappingURL=ObjectFieldComponents.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"base/Base/FieldRenderers/ObjectRenderer/ObjectFieldComponents.js","sources":["../../../../../src/base/Base/FieldRenderers/ObjectRenderer/ObjectFieldComponents.tsx"],"sourcesContent":["import React, { useEffect, useState } from \"react\";\nimport { observer } from \"mobx-react-lite\";\nimport { Accordion, Button, IconButton } from \"@webiny/admin-ui\";\nimport { ReactComponent as AddIcon } from \"@webiny/icons/add.svg\";\nimport { ReactComponent as DeleteIcon } from \"@webiny/icons/delete_outline.svg\";\nimport { ReactComponent as ArrowUp } from \"@webiny/icons/arrow_upward.svg\";\nimport { ReactComponent as ArrowDown } from \"@webiny/icons/arrow_downward.svg\";\nimport type { IObjectFieldItemVM, LayoutNodeVM } from \"~/features/formModel/index.js\";\nimport { hasSubtreeFocusRequest } from \"~/features/formModel/index.js\";\nimport { LayoutNodeRenderer } from \"~/features/formModel/FormView.js\";\nimport { resolveItemTitle, resolveItemDescription } from \"./resolveItemTitle.js\";\nimport type { ISortableItemProps } from \"~/presentation/sortable/index.js\";\n\nexport const NestedLayout = observer(({ layout }: { layout: LayoutNodeVM[] }) => {\n return (\n <div className={\"flex flex-col gap-md\"}>\n {layout.map((node, index) => (\n <LayoutNodeRenderer key={index} node={node} />\n ))}\n </div>\n );\n});\n\nexport interface ListItemRendererProps {\n item: IObjectFieldItemVM;\n index: number;\n total: number;\n label?: string;\n itemTitle?: string | ((data: Record<string, unknown>, index: number) => string);\n itemDescription?: string | ((data: Record<string, unknown>, index: number) => string);\n disabled: boolean;\n sortable?: ISortableItemProps;\n}\n\nexport const ListItemRenderer = observer(\n ({\n item,\n index,\n total,\n label,\n itemTitle,\n itemDescription,\n disabled,\n sortable\n }: ListItemRendererProps) => {\n const [open, setOpen] = useState(false);\n const hasFocusRequest = hasSubtreeFocusRequest(item.fields);\n\n useEffect(() => {\n if (hasFocusRequest) {\n setOpen(true);\n }\n }, [hasFocusRequest]);\n\n const actions = (\n <>\n <IconButton\n icon={<ArrowDown />}\n onClick={e => {\n e.stopPropagation();\n item.moveDown();\n }}\n variant=\"ghost\"\n disabled={index === total - 1}\n />\n <IconButton\n icon={<ArrowUp />}\n onClick={e => {\n e.stopPropagation();\n item.moveUp();\n }}\n variant=\"ghost\"\n disabled={index === 0}\n />\n <Accordion.Item.Action.Separator />\n <IconButton\n icon={<DeleteIcon />}\n onClick={e => {\n e.stopPropagation();\n item.remove();\n }}\n variant=\"ghost\"\n />\n </>\n );\n\n const accordion = (\n <Accordion\n background={\"base\"}\n variant={\"container\"}\n openClosedIndicatorPosition={\"left\"}\n >\n <Accordion.Item\n title={resolveItemTitle(item, index, label, itemTitle)}\n description={resolveItemDescription(item, index, itemDescription)}\n actions={disabled ? null : actions}\n open={open}\n onOpenChange={setOpen}\n draggable={!disabled && sortable !== undefined}\n dragHandleRef={sortable?.handleRef}\n >\n <NestedLayout layout={item.layout} />\n </Accordion.Item>\n </Accordion>\n );\n\n if (!sortable) {\n return accordion;\n }\n\n return (\n <div\n ref={sortable.ref}\n data-sortable-item=\"\"\n className={\"relative\"}\n style={{ opacity: sortable.isDragging ? 0.4 : 1 }}\n >\n {sortable.closestEdge === \"top\" && (\n <DropIndicator position={\"top\"} isFirst={index === 0} />\n )}\n {accordion}\n {sortable.closestEdge === \"bottom\" && (\n <DropIndicator position={\"bottom\"} isLast={index === total - 1} />\n )}\n </div>\n );\n }\n);\n\nconst DropIndicator = ({\n position,\n isFirst = false,\n isLast = false\n}: {\n position: \"top\" | \"bottom\";\n isFirst?: boolean;\n isLast?: boolean;\n}) => {\n let offset = \"calc(var(--spacing-md) / -2)\";\n if (position === \"top\" && isFirst) {\n offset = \"0px\";\n }\n if (position === \"bottom\" && isLast) {\n offset = \"0px\";\n }\n\n return (\n <div\n className={\"absolute left-0 right-0 z-10\"}\n style={{\n [position]: offset,\n transform: position === \"top\" ? \"translateY(-50%)\" : \"translateY(50%)\"\n }}\n >\n <div className={\"w-full h-[2px] bg-primary rounded-full\"} />\n </div>\n );\n};\n\nexport interface AddItemButtonProps {\n label?: string;\n disabled: boolean;\n onAdd: () => void;\n}\n\nexport const AddItemButton = ({ label, disabled, onAdd }: AddItemButtonProps) => {\n return (\n <div>\n <Button\n icon={<AddIcon />}\n text={label || \"Add Item\"}\n variant={\"tertiary\"}\n onClick={onAdd}\n disabled={disabled}\n />\n </div>\n );\n};\n"],"names":["NestedLayout","observer","layout","node","index","LayoutNodeRenderer","ListItemRenderer","item","total","label","itemTitle","itemDescription","disabled","sortable","open","setOpen","useState","hasFocusRequest","hasSubtreeFocusRequest","useEffect","actions","IconButton","ArrowDown","e","ArrowUp","Accordion","DeleteIcon","accordion","resolveItemTitle","resolveItemDescription","undefined","DropIndicator","position","isFirst","isLast","offset","AddItemButton","onAdd","Button","AddIcon"],"mappings":"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"}
1
+ {"version":3,"file":"base/Base/FieldRenderers/ObjectRenderer/ObjectFieldComponents.js","sources":["../../../../../src/base/Base/FieldRenderers/ObjectRenderer/ObjectFieldComponents.tsx"],"sourcesContent":["import React, { useEffect, useMemo, useState } from \"react\";\nimport { observer } from \"mobx-react-lite\";\nimport { Accordion, Button, IconButton } from \"@webiny/admin-ui\";\nimport { ReactComponent as AddIcon } from \"@webiny/icons/add.svg\";\nimport { ReactComponent as DeleteIcon } from \"@webiny/icons/delete_outline.svg\";\nimport { ReactComponent as ArrowUp } from \"@webiny/icons/arrow_upward.svg\";\nimport { ReactComponent as ArrowDown } from \"@webiny/icons/arrow_downward.svg\";\nimport { useContainer } from \"@webiny/app\";\nimport type { IObjectFieldItemVM, LayoutNodeVM } from \"~/features/formModel/index.js\";\nimport { FieldValueFormatter, hasSubtreeFocusRequest } from \"~/features/formModel/index.js\";\nimport { LayoutNodeRenderer } from \"~/features/formModel/FormView.js\";\nimport { resolveItemTitle, resolveItemDescription } from \"./resolveItemTitle.js\";\nimport type { ISortableItemProps } from \"~/presentation/sortable/index.js\";\n\nexport const NestedLayout = observer(({ layout }: { layout: LayoutNodeVM[] }) => {\n return (\n <div className={\"flex flex-col gap-md\"}>\n {layout.map((node, index) => (\n <LayoutNodeRenderer key={index} node={node} />\n ))}\n </div>\n );\n});\n\nexport function useFieldValueFormatters(): FieldValueFormatter.Interface[] {\n const container = useContainer();\n return useMemo(() => container.resolveAll(FieldValueFormatter), [container]);\n}\n\nexport interface ListItemRendererProps {\n item: IObjectFieldItemVM;\n index: number;\n total: number;\n label?: string;\n itemTitle?: string | ((data: Record<string, unknown>, index: number) => string);\n itemDescription?: string | ((data: Record<string, unknown>, index: number) => string);\n disabled: boolean;\n sortable?: ISortableItemProps;\n}\n\nexport const ListItemRenderer = observer(\n ({\n item,\n index,\n total,\n label,\n itemTitle,\n itemDescription,\n disabled,\n sortable\n }: ListItemRendererProps) => {\n const [open, setOpen] = useState(false);\n const formatters = useFieldValueFormatters();\n const hasFocusRequest = hasSubtreeFocusRequest(item.fields);\n\n useEffect(() => {\n if (hasFocusRequest) {\n setOpen(true);\n }\n }, [hasFocusRequest]);\n\n const actions = (\n <>\n <IconButton\n icon={<ArrowDown />}\n onClick={e => {\n e.stopPropagation();\n item.moveDown();\n }}\n variant=\"ghost\"\n disabled={index === total - 1}\n />\n <IconButton\n icon={<ArrowUp />}\n onClick={e => {\n e.stopPropagation();\n item.moveUp();\n }}\n variant=\"ghost\"\n disabled={index === 0}\n />\n <Accordion.Item.Action.Separator />\n <IconButton\n icon={<DeleteIcon />}\n onClick={e => {\n e.stopPropagation();\n item.remove();\n }}\n variant=\"ghost\"\n />\n </>\n );\n\n const accordion = (\n <Accordion\n background={\"base\"}\n variant={\"container\"}\n openClosedIndicatorPosition={\"left\"}\n >\n <Accordion.Item\n title={resolveItemTitle(\n item.fields,\n index,\n `${label || \"Item\"} #${index + 1}`,\n itemTitle,\n formatters\n )}\n description={resolveItemDescription(\n item.fields,\n index,\n itemDescription,\n formatters\n )}\n actions={disabled ? null : actions}\n open={open}\n onOpenChange={setOpen}\n draggable={!disabled && sortable !== undefined}\n dragHandleRef={sortable?.handleRef}\n >\n <NestedLayout layout={item.layout} />\n </Accordion.Item>\n </Accordion>\n );\n\n if (!sortable) {\n return accordion;\n }\n\n return (\n <div\n ref={sortable.ref}\n data-sortable-item=\"\"\n className={\"relative\"}\n style={{ opacity: sortable.isDragging ? 0.4 : 1 }}\n >\n {sortable.closestEdge === \"top\" && (\n <DropIndicator position={\"top\"} isFirst={index === 0} />\n )}\n {accordion}\n {sortable.closestEdge === \"bottom\" && (\n <DropIndicator position={\"bottom\"} isLast={index === total - 1} />\n )}\n </div>\n );\n }\n);\n\nconst DropIndicator = ({\n position,\n isFirst = false,\n isLast = false\n}: {\n position: \"top\" | \"bottom\";\n isFirst?: boolean;\n isLast?: boolean;\n}) => {\n let offset = \"calc(var(--spacing-md) / -2)\";\n if (position === \"top\" && isFirst) {\n offset = \"0px\";\n }\n if (position === \"bottom\" && isLast) {\n offset = \"0px\";\n }\n\n return (\n <div\n className={\"absolute left-0 right-0 z-10\"}\n style={{\n [position]: offset,\n transform: position === \"top\" ? \"translateY(-50%)\" : \"translateY(50%)\"\n }}\n >\n <div className={\"w-full h-[2px] bg-primary rounded-full\"} />\n </div>\n );\n};\n\nexport interface AddItemButtonProps {\n label?: string;\n disabled: boolean;\n onAdd: () => void;\n}\n\nexport const AddItemButton = ({ label, disabled, onAdd }: AddItemButtonProps) => {\n return (\n <div>\n <Button\n icon={<AddIcon />}\n text={label || \"Add Item\"}\n variant={\"tertiary\"}\n onClick={onAdd}\n disabled={disabled}\n />\n </div>\n );\n};\n"],"names":["NestedLayout","observer","layout","node","index","LayoutNodeRenderer","useFieldValueFormatters","container","useContainer","useMemo","FieldValueFormatter","ListItemRenderer","item","total","label","itemTitle","itemDescription","disabled","sortable","open","setOpen","useState","formatters","hasFocusRequest","hasSubtreeFocusRequest","useEffect","actions","IconButton","ArrowDown","e","ArrowUp","Accordion","DeleteIcon","accordion","resolveItemTitle","resolveItemDescription","undefined","DropIndicator","position","isFirst","isLast","offset","AddItemButton","onAdd","Button","AddIcon"],"mappings":"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"}
@@ -1,33 +1,12 @@
1
1
  import react, { useEffect, useState } from "react";
2
2
  import { Accordion } from "@webiny/admin-ui";
3
3
  import { createObjectFieldRenderer } from "../../../../features/formModel/createFieldRenderer.js";
4
- import { NestedLayout } from "./ObjectFieldComponents.js";
4
+ import { NestedLayout, useFieldValueFormatters } from "./ObjectFieldComponents.js";
5
+ import { resolveItemDescription, resolveItemTitle } from "./resolveItemTitle.js";
5
6
  import { hasSubtreeFocusRequest } from "../../../../features/formModel/index.js";
6
- function resolveTitle(field, itemTitle) {
7
- if (!itemTitle) return field.label;
8
- if ("string" == typeof itemTitle) {
9
- const child = field.fields.find((f)=>f.name === itemTitle);
10
- return (child ? String(child.value ?? "") : "") || field.label;
11
- }
12
- const data = {};
13
- for (const child of field.fields)data[child.name] = child.value;
14
- return itemTitle(data) || field.label;
15
- }
16
- function resolveDescription(field, itemDescription) {
17
- if (!itemDescription) return;
18
- if ("string" == typeof itemDescription) {
19
- const child = field.fields.find((f)=>f.name === itemDescription);
20
- return (child ? String(child.value ?? "") : "") || void 0;
21
- }
22
- const data = {};
23
- for (const child of field.fields)data[child.name] = child.value;
24
- return itemDescription(data) || void 0;
25
- }
26
7
  const ObjectRenderer = createObjectFieldRenderer(({ field })=>{
27
8
  const settings = field.rendererSettings;
28
- if (settings?.container === false) return /*#__PURE__*/ react.createElement(NestedLayout, {
29
- layout: field.layout
30
- });
9
+ const formatters = useFieldValueFormatters();
31
10
  const focusInside = hasSubtreeFocusRequest(field.fields);
32
11
  const [open, setOpen] = useState(settings?.open ?? true);
33
12
  useEffect(()=>{
@@ -35,12 +14,15 @@ const ObjectRenderer = createObjectFieldRenderer(({ field })=>{
35
14
  }, [
36
15
  focusInside
37
16
  ]);
17
+ if (settings?.container === false) return /*#__PURE__*/ react.createElement(NestedLayout, {
18
+ layout: field.layout
19
+ });
38
20
  return /*#__PURE__*/ react.createElement(Accordion, {
39
21
  background: "base",
40
22
  variant: "container"
41
23
  }, /*#__PURE__*/ react.createElement(Accordion.Item, {
42
- title: resolveTitle(field, settings?.itemTitle),
43
- description: resolveDescription(field, settings?.itemDescription),
24
+ title: resolveItemTitle(field.fields, 0, field.label || "", settings?.itemTitle, formatters),
25
+ description: resolveItemDescription(field.fields, 0, settings?.itemDescription, formatters),
44
26
  open: open,
45
27
  onOpenChange: setOpen,
46
28
  className: "pl-sm"
@@ -1 +1 @@
1
- {"version":3,"file":"base/Base/FieldRenderers/ObjectRenderer/ObjectRenderer.js","sources":["../../../../../src/base/Base/FieldRenderers/ObjectRenderer/ObjectRenderer.tsx"],"sourcesContent":["import React, { useEffect, useState } from \"react\";\nimport { Accordion } from \"@webiny/admin-ui\";\nimport { createObjectFieldRenderer } from \"~/features/formModel/createFieldRenderer.js\";\nimport { NestedLayout } from \"./ObjectFieldComponents.js\";\nimport type { IObjectFieldVM } from \"~/features/formModel/index.js\";\nimport { hasSubtreeFocusRequest } from \"~/features/formModel/index.js\";\n\ndeclare module \"../../../../features/formModel/abstractions.js\" {\n interface IFieldRendererRegistry {\n objectAccordionSingle: {\n fieldType: \"object\";\n settings?: {\n open?: boolean;\n container?: boolean;\n itemTitle?: string | ((data: Record<string, unknown>) => string);\n itemDescription?: string | ((data: Record<string, unknown>) => string);\n };\n };\n }\n}\n\nfunction resolveTitle(\n field: IObjectFieldVM,\n itemTitle: string | ((data: Record<string, unknown>) => string) | undefined\n): string | undefined {\n if (!itemTitle) {\n return field.label;\n }\n\n if (typeof itemTitle === \"string\") {\n const child = field.fields.find(f => f.name === itemTitle);\n return (child ? String(child.value ?? \"\") : \"\") || field.label;\n }\n\n const data: Record<string, unknown> = {};\n for (const child of field.fields) {\n data[child.name] = child.value;\n }\n return itemTitle(data) || field.label;\n}\n\nfunction resolveDescription(\n field: IObjectFieldVM,\n itemDescription: string | ((data: Record<string, unknown>) => string) | undefined\n): string | undefined {\n if (!itemDescription) {\n return undefined;\n }\n\n if (typeof itemDescription === \"string\") {\n const child = field.fields.find(f => f.name === itemDescription);\n return (child ? String(child.value ?? \"\") : \"\") || undefined;\n }\n\n const data: Record<string, unknown> = {};\n for (const child of field.fields) {\n data[child.name] = child.value;\n }\n return itemDescription(data) || undefined;\n}\n\nexport const ObjectRenderer = createObjectFieldRenderer<\"objectAccordionSingle\">(({ field }) => {\n const settings = field.rendererSettings;\n\n if (settings?.container === false) {\n return <NestedLayout layout={field.layout} />;\n }\n\n // Force the object accordion open when a focus request lands anywhere in\n // its subtree, so \"jump to field\" can cascade through collapsed levels.\n const focusInside = hasSubtreeFocusRequest(field.fields);\n const [open, setOpen] = useState(settings?.open ?? true);\n useEffect(() => {\n if (focusInside) {\n setOpen(true);\n }\n }, [focusInside]);\n\n return (\n <Accordion background={\"base\"} variant={\"container\"}>\n <Accordion.Item\n title={resolveTitle(field, settings?.itemTitle)}\n description={resolveDescription(field, settings?.itemDescription)}\n open={open}\n onOpenChange={setOpen}\n className={\"pl-sm\"}\n >\n <NestedLayout layout={field.layout} />\n </Accordion.Item>\n </Accordion>\n );\n});\n"],"names":["resolveTitle","field","itemTitle","child","f","String","data","resolveDescription","itemDescription","undefined","ObjectRenderer","createObjectFieldRenderer","settings","NestedLayout","focusInside","hasSubtreeFocusRequest","open","setOpen","useState","useEffect","Accordion"],"mappings":";;;;;AAqBA,SAASA,aACLC,KAAqB,EACrBC,SAA2E;IAE3E,IAAI,CAACA,WACD,OAAOD,MAAM,KAAK;IAGtB,IAAI,AAAqB,YAArB,OAAOC,WAAwB;QAC/B,MAAMC,QAAQF,MAAM,MAAM,CAAC,IAAI,CAACG,CAAAA,IAAKA,EAAE,IAAI,KAAKF;QAChD,OAAQC,AAAAA,CAAAA,QAAQE,OAAOF,MAAM,KAAK,IAAI,MAAM,EAAC,KAAMF,MAAM,KAAK;IAClE;IAEA,MAAMK,OAAgC,CAAC;IACvC,KAAK,MAAMH,SAASF,MAAM,MAAM,CAC5BK,IAAI,CAACH,MAAM,IAAI,CAAC,GAAGA,MAAM,KAAK;IAElC,OAAOD,UAAUI,SAASL,MAAM,KAAK;AACzC;AAEA,SAASM,mBACLN,KAAqB,EACrBO,eAAiF;IAEjF,IAAI,CAACA,iBACD;IAGJ,IAAI,AAA2B,YAA3B,OAAOA,iBAA8B;QACrC,MAAML,QAAQF,MAAM,MAAM,CAAC,IAAI,CAACG,CAAAA,IAAKA,EAAE,IAAI,KAAKI;QAChD,OAAQL,AAAAA,CAAAA,QAAQE,OAAOF,MAAM,KAAK,IAAI,MAAM,EAAC,KAAMM;IACvD;IAEA,MAAMH,OAAgC,CAAC;IACvC,KAAK,MAAMH,SAASF,MAAM,MAAM,CAC5BK,IAAI,CAACH,MAAM,IAAI,CAAC,GAAGA,MAAM,KAAK;IAElC,OAAOK,gBAAgBF,SAASG;AACpC;AAEO,MAAMC,iBAAiBC,0BAAmD,CAAC,EAAEV,KAAK,EAAE;IACvF,MAAMW,WAAWX,MAAM,gBAAgB;IAEvC,IAAIW,UAAU,cAAc,OACxB,OAAO,WAAP,GAAO,oBAACC,cAAYA;QAAC,QAAQZ,MAAM,MAAM;;IAK7C,MAAMa,cAAcC,uBAAuBd,MAAM,MAAM;IACvD,MAAM,CAACe,MAAMC,QAAQ,GAAGC,SAASN,UAAU,QAAQ;IACnDO,UAAU;QACN,IAAIL,aACAG,QAAQ;IAEhB,GAAG;QAACH;KAAY;IAEhB,OAAO,WAAP,GACI,oBAACM,WAASA;QAAC,YAAY;QAAQ,SAAS;qBACpC,oBAACA,UAAU,IAAI;QACX,OAAOpB,aAAaC,OAAOW,UAAU;QACrC,aAAaL,mBAAmBN,OAAOW,UAAU;QACjD,MAAMI;QACN,cAAcC;QACd,WAAW;qBAEX,oBAACJ,cAAYA;QAAC,QAAQZ,MAAM,MAAM;;AAIlD"}
1
+ {"version":3,"file":"base/Base/FieldRenderers/ObjectRenderer/ObjectRenderer.js","sources":["../../../../../src/base/Base/FieldRenderers/ObjectRenderer/ObjectRenderer.tsx"],"sourcesContent":["import React, { useEffect, useState } from \"react\";\nimport { Accordion } from \"@webiny/admin-ui\";\nimport { createObjectFieldRenderer } from \"~/features/formModel/createFieldRenderer.js\";\nimport { NestedLayout, useFieldValueFormatters } from \"./ObjectFieldComponents.js\";\nimport { resolveItemDescription, resolveItemTitle } from \"./resolveItemTitle.js\";\nimport { hasSubtreeFocusRequest } from \"~/features/formModel/index.js\";\n\ndeclare module \"../../../../features/formModel/abstractions.js\" {\n interface IFieldRendererRegistry {\n objectAccordionSingle: {\n fieldType: \"object\";\n settings?: {\n open?: boolean;\n container?: boolean;\n itemTitle?: string | ((data: Record<string, unknown>) => string);\n itemDescription?: string | ((data: Record<string, unknown>) => string);\n };\n };\n }\n}\n\nexport const ObjectRenderer = createObjectFieldRenderer<\"objectAccordionSingle\">(({ field }) => {\n const settings = field.rendererSettings;\n const formatters = useFieldValueFormatters();\n\n // Force the object accordion open when a focus request lands anywhere in\n // its subtree, so \"jump to field\" can cascade through collapsed levels.\n const focusInside = hasSubtreeFocusRequest(field.fields);\n const [open, setOpen] = useState(settings?.open ?? true);\n useEffect(() => {\n if (focusInside) {\n setOpen(true);\n }\n }, [focusInside]);\n\n if (settings?.container === false) {\n return <NestedLayout layout={field.layout} />;\n }\n\n return (\n <Accordion background={\"base\"} variant={\"container\"}>\n <Accordion.Item\n title={resolveItemTitle(\n field.fields,\n 0,\n field.label || \"\",\n settings?.itemTitle,\n formatters\n )}\n description={resolveItemDescription(\n field.fields,\n 0,\n settings?.itemDescription,\n formatters\n )}\n open={open}\n onOpenChange={setOpen}\n className={\"pl-sm\"}\n >\n <NestedLayout layout={field.layout} />\n </Accordion.Item>\n </Accordion>\n );\n});\n"],"names":["ObjectRenderer","createObjectFieldRenderer","field","settings","formatters","useFieldValueFormatters","focusInside","hasSubtreeFocusRequest","open","setOpen","useState","useEffect","NestedLayout","Accordion","resolveItemTitle","resolveItemDescription"],"mappings":";;;;;;AAqBO,MAAMA,iBAAiBC,0BAAmD,CAAC,EAAEC,KAAK,EAAE;IACvF,MAAMC,WAAWD,MAAM,gBAAgB;IACvC,MAAME,aAAaC;IAInB,MAAMC,cAAcC,uBAAuBL,MAAM,MAAM;IACvD,MAAM,CAACM,MAAMC,QAAQ,GAAGC,SAASP,UAAU,QAAQ;IACnDQ,UAAU;QACN,IAAIL,aACAG,QAAQ;IAEhB,GAAG;QAACH;KAAY;IAEhB,IAAIH,UAAU,cAAc,OACxB,OAAO,WAAP,GAAO,oBAACS,cAAYA;QAAC,QAAQV,MAAM,MAAM;;IAG7C,OAAO,WAAP,GACI,oBAACW,WAASA;QAAC,YAAY;QAAQ,SAAS;qBACpC,oBAACA,UAAU,IAAI;QACX,OAAOC,iBACHZ,MAAM,MAAM,EACZ,GACAA,MAAM,KAAK,IAAI,IACfC,UAAU,WACVC;QAEJ,aAAaW,uBACTb,MAAM,MAAM,EACZ,GACAC,UAAU,iBACVC;QAEJ,MAAMI;QACN,cAAcC;QACd,WAAW;qBAEX,oBAACG,cAAYA;QAAC,QAAQV,MAAM,MAAM;;AAIlD"}
@@ -1,5 +1,10 @@
1
- import type { IObjectFieldItemVM } from "../../../../features/formModel/index.js";
1
+ import type { IFieldValueFormatter, IFieldVM } from "../../../../features/formModel/index.js";
2
2
  type ItemStringSetting = string | ((data: Record<string, unknown>, index: number) => string);
3
- export declare function resolveItemTitle(item: IObjectFieldItemVM, index: number, label: string | undefined, itemTitle: ItemStringSetting | undefined): string;
4
- export declare function resolveItemDescription(item: IObjectFieldItemVM, index: number, itemDescription: ItemStringSetting | undefined): string | undefined;
3
+ /**
4
+ * The text a field's value reads as in a summary. A formatter registered for the field's type
5
+ * wins; otherwise each value is printed, swapped for its option label when the field has options.
6
+ */
7
+ export declare function formatFieldValue(field: IFieldVM, formatters: IFieldValueFormatter[]): string;
8
+ export declare function resolveItemTitle(fields: IFieldVM[], index: number, fallback: string, itemTitle: ItemStringSetting | undefined, formatters: IFieldValueFormatter[]): string;
9
+ export declare function resolveItemDescription(fields: IFieldVM[], index: number, itemDescription: ItemStringSetting | undefined, formatters: IFieldValueFormatter[]): string | undefined;
5
10
  export {};
@@ -3,26 +3,34 @@ function fieldsToData(fields) {
3
3
  for (const field of fields)data[field.name] = field.value;
4
4
  return data;
5
5
  }
6
- function resolveItemTitle(item, index, label, itemTitle) {
7
- const fallback = `${label || "Item"} #${index + 1}`;
8
- if (!itemTitle) return fallback;
9
- if ("string" == typeof itemTitle) {
10
- const field = item.fields.find((f)=>f.name === itemTitle);
11
- const value = field ? String(field.value ?? "") : "";
12
- return value || fallback;
6
+ function formatScalar(value, options) {
7
+ if (null == value || "object" == typeof value) return "";
8
+ const option = options?.find((o)=>String(o.value) === String(value));
9
+ return option ? option.label : String(value);
10
+ }
11
+ function formatFieldValue(field, formatters) {
12
+ const formatter = formatters.find((f)=>f.type === field.type);
13
+ if (formatter) return formatter.format(field) ?? "";
14
+ const values = Array.isArray(field.value) ? field.value : [
15
+ field.value
16
+ ];
17
+ return values.map((value)=>formatScalar(value, field.options)).filter(Boolean).join(", ");
18
+ }
19
+ function resolveSetting(fields, index, setting, formatters) {
20
+ if ("string" == typeof setting) {
21
+ const field = fields.find((f)=>f.name === setting);
22
+ return field ? formatFieldValue(field, formatters) : "";
13
23
  }
14
- const data = fieldsToData(item.fields);
15
- return itemTitle(data, index) || fallback;
24
+ return setting(fieldsToData(fields), index);
16
25
  }
17
- function resolveItemDescription(item, index, itemDescription) {
26
+ function resolveItemTitle(fields, index, fallback, itemTitle, formatters) {
27
+ if (!itemTitle) return fallback;
28
+ return resolveSetting(fields, index, itemTitle, formatters) || fallback;
29
+ }
30
+ function resolveItemDescription(fields, index, itemDescription, formatters) {
18
31
  if (!itemDescription) return;
19
- if ("string" == typeof itemDescription) {
20
- const field = item.fields.find((f)=>f.name === itemDescription);
21
- return field ? String(field.value ?? "") || void 0 : void 0;
22
- }
23
- const data = fieldsToData(item.fields);
24
- return itemDescription(data, index) || void 0;
32
+ return resolveSetting(fields, index, itemDescription, formatters) || void 0;
25
33
  }
26
- export { resolveItemDescription, resolveItemTitle };
34
+ export { formatFieldValue, resolveItemDescription, resolveItemTitle };
27
35
 
28
36
  //# sourceMappingURL=resolveItemTitle.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"base/Base/FieldRenderers/ObjectRenderer/resolveItemTitle.js","sources":["../../../../../src/base/Base/FieldRenderers/ObjectRenderer/resolveItemTitle.ts"],"sourcesContent":["import type { IFieldVM, IObjectFieldItemVM } from \"~/features/formModel/index.js\";\n\ntype ItemStringSetting = string | ((data: Record<string, unknown>, index: number) => string);\n\nfunction fieldsToData(fields: IFieldVM[]): Record<string, unknown> {\n const data: Record<string, unknown> = {};\n for (const field of fields) {\n data[field.name] = field.value;\n }\n return data;\n}\n\nexport function resolveItemTitle(\n item: IObjectFieldItemVM,\n index: number,\n label: string | undefined,\n itemTitle: ItemStringSetting | undefined\n): string {\n const fallback = `${label || \"Item\"} #${index + 1}`;\n\n if (!itemTitle) {\n return fallback;\n }\n\n if (typeof itemTitle === \"string\") {\n const field = item.fields.find(f => f.name === itemTitle);\n const value = field ? String(field.value ?? \"\") : \"\";\n return value || fallback;\n }\n\n const data = fieldsToData(item.fields);\n return itemTitle(data, index) || fallback;\n}\n\nexport function resolveItemDescription(\n item: IObjectFieldItemVM,\n index: number,\n itemDescription: ItemStringSetting | undefined\n): string | undefined {\n if (!itemDescription) {\n return undefined;\n }\n\n if (typeof itemDescription === \"string\") {\n const field = item.fields.find(f => f.name === itemDescription);\n return field ? String(field.value ?? \"\") || undefined : undefined;\n }\n\n const data = fieldsToData(item.fields);\n return itemDescription(data, index) || undefined;\n}\n"],"names":["fieldsToData","fields","data","field","resolveItemTitle","item","index","label","itemTitle","fallback","f","value","String","resolveItemDescription","itemDescription","undefined"],"mappings":"AAIA,SAASA,aAAaC,MAAkB;IACpC,MAAMC,OAAgC,CAAC;IACvC,KAAK,MAAMC,SAASF,OAChBC,IAAI,CAACC,MAAM,IAAI,CAAC,GAAGA,MAAM,KAAK;IAElC,OAAOD;AACX;AAEO,SAASE,iBACZC,IAAwB,EACxBC,KAAa,EACbC,KAAyB,EACzBC,SAAwC;IAExC,MAAMC,WAAW,GAAGF,SAAS,OAAO,EAAE,EAAED,QAAQ,GAAG;IAEnD,IAAI,CAACE,WACD,OAAOC;IAGX,IAAI,AAAqB,YAArB,OAAOD,WAAwB;QAC/B,MAAML,QAAQE,KAAK,MAAM,CAAC,IAAI,CAACK,CAAAA,IAAKA,EAAE,IAAI,KAAKF;QAC/C,MAAMG,QAAQR,QAAQS,OAAOT,MAAM,KAAK,IAAI,MAAM;QAClD,OAAOQ,SAASF;IACpB;IAEA,MAAMP,OAAOF,aAAaK,KAAK,MAAM;IACrC,OAAOG,UAAUN,MAAMI,UAAUG;AACrC;AAEO,SAASI,uBACZR,IAAwB,EACxBC,KAAa,EACbQ,eAA8C;IAE9C,IAAI,CAACA,iBACD;IAGJ,IAAI,AAA2B,YAA3B,OAAOA,iBAA8B;QACrC,MAAMX,QAAQE,KAAK,MAAM,CAAC,IAAI,CAACK,CAAAA,IAAKA,EAAE,IAAI,KAAKI;QAC/C,OAAOX,QAAQS,OAAOT,MAAM,KAAK,IAAI,OAAOY,SAAYA;IAC5D;IAEA,MAAMb,OAAOF,aAAaK,KAAK,MAAM;IACrC,OAAOS,gBAAgBZ,MAAMI,UAAUS;AAC3C"}
1
+ {"version":3,"file":"base/Base/FieldRenderers/ObjectRenderer/resolveItemTitle.js","sources":["../../../../../src/base/Base/FieldRenderers/ObjectRenderer/resolveItemTitle.ts"],"sourcesContent":["import type { IFieldValueFormatter, IFieldVM, IValueOption } from \"~/features/formModel/index.js\";\n\ntype ItemStringSetting = string | ((data: Record<string, unknown>, index: number) => string);\n\nfunction fieldsToData(fields: IFieldVM[]): Record<string, unknown> {\n const data: Record<string, unknown> = {};\n for (const field of fields) {\n data[field.name] = field.value;\n }\n return data;\n}\n\nfunction formatScalar(value: unknown, options: IValueOption[] | undefined): string {\n if (value === null || value === undefined || typeof value === \"object\") {\n return \"\";\n }\n // Predefined values store the option's value; the editor knows the option by its label.\n const option = options?.find(o => String(o.value) === String(value));\n return option ? option.label : String(value);\n}\n\n/**\n * The text a field's value reads as in a summary. A formatter registered for the field's type\n * wins; otherwise each value is printed, swapped for its option label when the field has options.\n */\nexport function formatFieldValue(field: IFieldVM, formatters: IFieldValueFormatter[]): string {\n const formatter = formatters.find(f => f.type === field.type);\n if (formatter) {\n return formatter.format(field) ?? \"\";\n }\n\n const values = Array.isArray(field.value) ? field.value : [field.value];\n return values\n .map(value => formatScalar(value, field.options))\n .filter(Boolean)\n .join(\", \");\n}\n\nfunction resolveSetting(\n fields: IFieldVM[],\n index: number,\n setting: ItemStringSetting,\n formatters: IFieldValueFormatter[]\n): string {\n if (typeof setting === \"string\") {\n const field = fields.find(f => f.name === setting);\n return field ? formatFieldValue(field, formatters) : \"\";\n }\n\n return setting(fieldsToData(fields), index);\n}\n\nexport function resolveItemTitle(\n fields: IFieldVM[],\n index: number,\n fallback: string,\n itemTitle: ItemStringSetting | undefined,\n formatters: IFieldValueFormatter[]\n): string {\n if (!itemTitle) {\n return fallback;\n }\n\n return resolveSetting(fields, index, itemTitle, formatters) || fallback;\n}\n\nexport function resolveItemDescription(\n fields: IFieldVM[],\n index: number,\n itemDescription: ItemStringSetting | undefined,\n formatters: IFieldValueFormatter[]\n): string | undefined {\n if (!itemDescription) {\n return undefined;\n }\n\n return resolveSetting(fields, index, itemDescription, formatters) || undefined;\n}\n"],"names":["fieldsToData","fields","data","field","formatScalar","value","options","option","o","String","formatFieldValue","formatters","formatter","f","values","Array","Boolean","resolveSetting","index","setting","resolveItemTitle","fallback","itemTitle","resolveItemDescription","itemDescription","undefined"],"mappings":"AAIA,SAASA,aAAaC,MAAkB;IACpC,MAAMC,OAAgC,CAAC;IACvC,KAAK,MAAMC,SAASF,OAChBC,IAAI,CAACC,MAAM,IAAI,CAAC,GAAGA,MAAM,KAAK;IAElC,OAAOD;AACX;AAEA,SAASE,aAAaC,KAAc,EAAEC,OAAmC;IACrE,IAAID,QAAAA,SAAyC,AAAiB,YAAjB,OAAOA,OAChD,OAAO;IAGX,MAAME,SAASD,SAAS,KAAKE,CAAAA,IAAKC,OAAOD,EAAE,KAAK,MAAMC,OAAOJ;IAC7D,OAAOE,SAASA,OAAO,KAAK,GAAGE,OAAOJ;AAC1C;AAMO,SAASK,iBAAiBP,KAAe,EAAEQ,UAAkC;IAChF,MAAMC,YAAYD,WAAW,IAAI,CAACE,CAAAA,IAAKA,EAAE,IAAI,KAAKV,MAAM,IAAI;IAC5D,IAAIS,WACA,OAAOA,UAAU,MAAM,CAACT,UAAU;IAGtC,MAAMW,SAASC,MAAM,OAAO,CAACZ,MAAM,KAAK,IAAIA,MAAM,KAAK,GAAG;QAACA,MAAM,KAAK;KAAC;IACvE,OAAOW,OACF,GAAG,CAACT,CAAAA,QAASD,aAAaC,OAAOF,MAAM,OAAO,GAC9C,MAAM,CAACa,SACP,IAAI,CAAC;AACd;AAEA,SAASC,eACLhB,MAAkB,EAClBiB,KAAa,EACbC,OAA0B,EAC1BR,UAAkC;IAElC,IAAI,AAAmB,YAAnB,OAAOQ,SAAsB;QAC7B,MAAMhB,QAAQF,OAAO,IAAI,CAACY,CAAAA,IAAKA,EAAE,IAAI,KAAKM;QAC1C,OAAOhB,QAAQO,iBAAiBP,OAAOQ,cAAc;IACzD;IAEA,OAAOQ,QAAQnB,aAAaC,SAASiB;AACzC;AAEO,SAASE,iBACZnB,MAAkB,EAClBiB,KAAa,EACbG,QAAgB,EAChBC,SAAwC,EACxCX,UAAkC;IAElC,IAAI,CAACW,WACD,OAAOD;IAGX,OAAOJ,eAAehB,QAAQiB,OAAOI,WAAWX,eAAeU;AACnE;AAEO,SAASE,uBACZtB,MAAkB,EAClBiB,KAAa,EACbM,eAA8C,EAC9Cb,UAAkC;IAElC,IAAI,CAACa,iBACD;IAGJ,OAAOP,eAAehB,QAAQiB,OAAOM,iBAAiBb,eAAec;AACzE"}