@multiplatform.one/frappe-ui 7.7.6 → 7.8.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.
- package/dist/cjs/ChartCard.native.js.map +1 -1
- package/dist/cjs/FileBrowser/AttachLibrary.native.js.map +1 -1
- package/dist/cjs/FileBrowser/folderTree.native.js.map +1 -1
- package/dist/cjs/FileBrowser/index.native.js.map +1 -1
- package/dist/cjs/FileBrowser/types.native.js.map +1 -1
- package/dist/cjs/FileBrowser/viewPersistence.native.js.map +1 -1
- package/dist/cjs/FormSidebar.native.js.map +1 -1
- package/dist/cjs/FrappeAssignment.native.js.map +1 -1
- package/dist/cjs/FrappeCalendar.cjs +20 -17
- package/dist/cjs/FrappeCalendar.native.js +22 -21
- package/dist/cjs/FrappeCalendar.native.js.map +1 -1
- package/dist/cjs/FrappeChartCard.native.js.map +1 -1
- package/dist/cjs/FrappeConnectionIndicator.native.js.map +1 -1
- package/dist/cjs/FrappeDashboard.native.js.map +1 -1
- package/dist/cjs/FrappeForm.cjs +1 -1
- package/dist/cjs/FrappeForm.native.js +2 -2
- package/dist/cjs/FrappeForm.native.js.map +1 -1
- package/dist/cjs/FrappeGantt.cjs +25 -34
- package/dist/cjs/FrappeGantt.native.js +27 -38
- package/dist/cjs/FrappeGantt.native.js.map +1 -1
- package/dist/cjs/FrappeImageView.cjs +4 -2
- package/dist/cjs/FrappeImageView.native.js +8 -4
- package/dist/cjs/FrappeImageView.native.js.map +1 -1
- package/dist/cjs/FrappeKPICard.cjs +26 -1
- package/dist/cjs/FrappeKPICard.native.js +26 -1
- package/dist/cjs/FrappeKPICard.native.js.map +1 -1
- package/dist/cjs/FrappeKanban.cjs +16 -17
- package/dist/cjs/FrappeKanban.native.js +18 -21
- package/dist/cjs/FrappeKanban.native.js.map +1 -1
- package/dist/cjs/FrappeLikedBy.cjs +1 -1
- package/dist/cjs/FrappeLikedBy.native.js +1 -1
- package/dist/cjs/FrappeLikedBy.native.js.map +1 -1
- package/dist/cjs/FrappeList.cjs +2 -2
- package/dist/cjs/FrappeList.native.js +2 -2
- package/dist/cjs/FrappeList.native.js.map +1 -1
- package/dist/cjs/FrappeListView.native.js.map +1 -1
- package/dist/cjs/FrappeProvider.native.js.map +1 -1
- package/dist/cjs/FrappeReconnectIndicator.native.js.map +1 -1
- package/dist/cjs/FrappeReportBuilder.cjs +2 -2
- package/dist/cjs/FrappeReportBuilder.native.js +2 -2
- package/dist/cjs/FrappeReportBuilder.native.js.map +1 -1
- package/dist/cjs/FrappeReportView.native.js +3 -3
- package/dist/cjs/FrappeReportView.native.js.map +1 -1
- package/dist/cjs/FrappeTable.cjs +35 -38
- package/dist/cjs/FrappeTable.native.js +38 -39
- package/dist/cjs/FrappeTable.native.js.map +1 -1
- package/dist/cjs/FrappeTreeView.cjs +85 -50
- package/dist/cjs/FrappeTreeView.native.js +129 -50
- package/dist/cjs/FrappeTreeView.native.js.map +1 -1
- package/dist/cjs/SyncErrorNotice.native.js.map +1 -1
- package/dist/cjs/commentActions.cjs +1 -1
- package/dist/cjs/commentActions.native.js +1 -1
- package/dist/cjs/commentActions.native.js.map +1 -1
- package/dist/cjs/components/attachments.native.js.map +1 -1
- package/dist/cjs/components/tags.native.js.map +1 -1
- package/dist/cjs/components/timeline.cjs +1 -1
- package/dist/cjs/components/timeline.native.js +1 -1
- package/dist/cjs/components/timeline.native.js.map +1 -1
- package/dist/cjs/dependsOn.native.js.map +1 -1
- package/dist/cjs/desk/DeskFormPage.native.js.map +1 -1
- package/dist/cjs/desk/DeskFormSidebar.native.js.map +1 -1
- package/dist/cjs/desk/DeskListPage.native.js.map +1 -1
- package/dist/cjs/desk/DeskShell.cjs +2 -0
- package/dist/cjs/desk/DeskShell.native.js +2 -0
- package/dist/cjs/desk/DeskShell.native.js.map +1 -1
- package/dist/cjs/desk/DeskSidebar.native.js.map +1 -1
- package/dist/cjs/desk/awesomebar.cjs +1 -1
- package/dist/cjs/desk/awesomebar.native.js +1 -1
- package/dist/cjs/desk/awesomebar.native.js.map +1 -1
- package/dist/cjs/fieldOverrides.native.js.map +1 -1
- package/dist/cjs/fieldRegistry.native.js +31 -31
- package/dist/cjs/fieldRegistry.native.js.map +1 -1
- package/dist/cjs/fields/Barcode/index.native.js.map +1 -1
- package/dist/cjs/fields/Check/index.native.js.map +1 -1
- package/dist/cjs/fields/CodeEditor/index.native.js.map +1 -1
- package/dist/cjs/fields/Color/index.native.js.map +1 -1
- package/dist/cjs/fields/Data/index.native.js.map +1 -1
- package/dist/cjs/fields/Date/sheetKeyboard.native.js.map +1 -1
- package/dist/cjs/fields/Date/types.native.js.map +1 -1
- package/dist/cjs/fields/Date/wire.native.js.map +1 -1
- package/dist/cjs/fields/DateRange/index.native.js.map +1 -1
- package/dist/cjs/fields/Duration/index.native.js.map +1 -1
- package/dist/cjs/fields/DynamicLink.native.js.map +1 -1
- package/dist/cjs/fields/FileUpload.native.js.map +1 -1
- package/dist/cjs/fields/FileUploadField/index.native.js.map +1 -1
- package/dist/cjs/fields/Geolocation/index.native.js.map +1 -1
- package/dist/cjs/fields/HTML/index.native.js.map +1 -1
- package/dist/cjs/fields/Heading/index.native.js.map +1 -1
- package/dist/cjs/fields/Icon/index.native.js.map +1 -1
- package/dist/cjs/fields/Link.native.js.map +1 -1
- package/dist/cjs/fields/LinkTree.native.js.map +1 -1
- package/dist/cjs/fields/LongText/index.native.js.map +1 -1
- package/dist/cjs/fields/MentionInput/index.cjs +4 -1
- package/dist/cjs/fields/MentionInput/index.native.js +4 -1
- package/dist/cjs/fields/MentionInput/index.native.js.map +1 -1
- package/dist/cjs/fields/Numeric/index.native.js.map +1 -1
- package/dist/cjs/fields/OTPInput/index.native.js.map +1 -1
- package/dist/cjs/fields/Password/index.native.js.map +1 -1
- package/dist/cjs/fields/Phone/index.native.js.map +1 -1
- package/dist/cjs/fields/Progress/index.native.js.map +1 -1
- package/dist/cjs/fields/RadioGroup/index.native.js.map +1 -1
- package/dist/cjs/fields/Rating/index.native.js.map +1 -1
- package/dist/cjs/fields/ReadOnly/index.native.js.map +1 -1
- package/dist/cjs/fields/Select/index.native.js.map +1 -1
- package/dist/cjs/fields/Signature/index.native.js.map +1 -1
- package/dist/cjs/fields/Slider/index.native.js.map +1 -1
- package/dist/cjs/fields/SmallText/index.native.js.map +1 -1
- package/dist/cjs/fields/Switch/index.native.js.map +1 -1
- package/dist/cjs/fields/TableMultiSelect.native.js.map +1 -1
- package/dist/cjs/fields/TextEditor/types.native.js.map +1 -1
- package/dist/cjs/fields/TimePicker/index.native.js.map +1 -1
- package/dist/cjs/fields/ToggleGroup/index.native.js.map +1 -1
- package/dist/cjs/fields/shared/EditorPreviewPanes.native.js.map +1 -1
- package/dist/cjs/filterFields.native.js.map +1 -1
- package/dist/cjs/formEngine/createPrefill.native.js.map +1 -1
- package/dist/cjs/formEngine/dirtyTracking.native.js.map +1 -1
- package/dist/cjs/formEngine/doctypeMeta.native.js.map +1 -1
- package/dist/cjs/formEngine/fetchFrom.native.js.map +1 -1
- package/dist/cjs/formEngine/fieldPermissions.native.js.map +1 -1
- package/dist/cjs/formEngine/formActions.native.js.map +1 -1
- package/dist/cjs/formEngine/formLayout.native.js.map +1 -1
- package/dist/cjs/formEngine/index.native.js.map +1 -1
- package/dist/cjs/formEngine/useFormEngine.native.js.map +1 -1
- package/dist/cjs/frappeEventColor.native.js.map +1 -1
- package/dist/cjs/frappeStoryTarget.native.js.map +1 -1
- package/dist/cjs/frappeUsers.native.js.map +1 -1
- package/dist/cjs/index.cjs +4 -0
- package/dist/cjs/index.native.js +38 -34
- package/dist/cjs/index.native.js.map +1 -1
- package/dist/cjs/liveErrors.native.js.map +1 -1
- package/dist/cjs/resolveControl.native.js.map +1 -1
- package/dist/cjs/schemaForm/SchemaWizard.native.js.map +1 -1
- package/dist/cjs/schemaForm/applyFieldOrder.native.js.map +1 -1
- package/dist/cjs/schemaForm/index.cjs +10 -9
- package/dist/cjs/schemaForm/index.native.js +11 -10
- package/dist/cjs/schemaForm/index.native.js.map +1 -1
- package/dist/cjs/shared/devWarn.cjs +40 -0
- package/dist/cjs/shared/devWarn.native.js +45 -0
- package/dist/cjs/shared/devWarn.native.js.map +1 -0
- package/dist/cjs/shared/useKeyboardInset.native.js.map +1 -1
- package/dist/cjs/syncError.native.js.map +1 -1
- package/dist/cjs/useFrappeAggregate.cjs +15 -8
- package/dist/cjs/useFrappeAggregate.native.js +15 -9
- package/dist/cjs/useFrappeAggregate.native.js.map +1 -1
- package/dist/cjs/useFrappeFacets.cjs +10 -9
- package/dist/cjs/useFrappeFacets.native.js +10 -10
- package/dist/cjs/useFrappeFacets.native.js.map +1 -1
- package/dist/cjs/useFrappePagination.native.js.map +1 -1
- package/dist/cjs/useFrappeRows.cjs +113 -0
- package/dist/cjs/useFrappeRows.native.js +129 -0
- package/dist/cjs/useFrappeRows.native.js.map +1 -0
- package/dist/esm/ChartCard.native.js.map +1 -1
- package/dist/esm/FileBrowser/AttachLibrary.native.js.map +1 -1
- package/dist/esm/FileBrowser/folderTree.native.js.map +1 -1
- package/dist/esm/FileBrowser/index.native.js.map +1 -1
- package/dist/esm/FileBrowser/types.native.js.map +1 -1
- package/dist/esm/FileBrowser/viewPersistence.native.js.map +1 -1
- package/dist/esm/FormSidebar.native.js.map +1 -1
- package/dist/esm/FrappeAssignment.native.js.map +1 -1
- package/dist/esm/FrappeCalendar.mjs +20 -17
- package/dist/esm/FrappeCalendar.mjs.map +1 -1
- package/dist/esm/FrappeCalendar.native.js +22 -21
- package/dist/esm/FrappeCalendar.native.js.map +1 -1
- package/dist/esm/FrappeChartCard.native.js.map +1 -1
- package/dist/esm/FrappeConnectionIndicator.native.js.map +1 -1
- package/dist/esm/FrappeDashboard.native.js.map +1 -1
- package/dist/esm/FrappeForm.mjs +1 -1
- package/dist/esm/FrappeForm.mjs.map +1 -1
- package/dist/esm/FrappeForm.native.js +1 -1
- package/dist/esm/FrappeForm.native.js.map +1 -1
- package/dist/esm/FrappeGantt.mjs +27 -36
- package/dist/esm/FrappeGantt.mjs.map +1 -1
- package/dist/esm/FrappeGantt.native.js +29 -40
- package/dist/esm/FrappeGantt.native.js.map +1 -1
- package/dist/esm/FrappeImageView.mjs +5 -3
- package/dist/esm/FrappeImageView.mjs.map +1 -1
- package/dist/esm/FrappeImageView.native.js +9 -5
- package/dist/esm/FrappeImageView.native.js.map +1 -1
- package/dist/esm/FrappeKPICard.mjs +27 -2
- package/dist/esm/FrappeKPICard.mjs.map +1 -1
- package/dist/esm/FrappeKPICard.native.js +27 -2
- package/dist/esm/FrappeKPICard.native.js.map +1 -1
- package/dist/esm/FrappeKanban.mjs +16 -17
- package/dist/esm/FrappeKanban.mjs.map +1 -1
- package/dist/esm/FrappeKanban.native.js +18 -21
- package/dist/esm/FrappeKanban.native.js.map +1 -1
- package/dist/esm/FrappeLikedBy.mjs +1 -1
- package/dist/esm/FrappeLikedBy.mjs.map +1 -1
- package/dist/esm/FrappeLikedBy.native.js +1 -1
- package/dist/esm/FrappeLikedBy.native.js.map +1 -1
- package/dist/esm/FrappeList.mjs +2 -2
- package/dist/esm/FrappeList.mjs.map +1 -1
- package/dist/esm/FrappeList.native.js +2 -2
- package/dist/esm/FrappeList.native.js.map +1 -1
- package/dist/esm/FrappeListView.native.js.map +1 -1
- package/dist/esm/FrappeProvider.mjs.map +1 -1
- package/dist/esm/FrappeProvider.native.js.map +1 -1
- package/dist/esm/FrappeReconnectIndicator.native.js.map +1 -1
- package/dist/esm/FrappeReportBuilder.mjs +2 -2
- package/dist/esm/FrappeReportBuilder.mjs.map +1 -1
- package/dist/esm/FrappeReportBuilder.native.js +2 -2
- package/dist/esm/FrappeReportBuilder.native.js.map +1 -1
- package/dist/esm/FrappeReportView.native.js.map +1 -1
- package/dist/esm/FrappeTable.mjs +36 -39
- package/dist/esm/FrappeTable.mjs.map +1 -1
- package/dist/esm/FrappeTable.native.js +39 -40
- package/dist/esm/FrappeTable.native.js.map +1 -1
- package/dist/esm/FrappeTreeView.mjs +89 -55
- package/dist/esm/FrappeTreeView.mjs.map +1 -1
- package/dist/esm/FrappeTreeView.native.js +133 -55
- package/dist/esm/FrappeTreeView.native.js.map +1 -1
- package/dist/esm/SyncErrorNotice.native.js.map +1 -1
- package/dist/esm/commentActions.mjs +1 -1
- package/dist/esm/commentActions.mjs.map +1 -1
- package/dist/esm/commentActions.native.js +1 -1
- package/dist/esm/commentActions.native.js.map +1 -1
- package/dist/esm/components/attachments.native.js.map +1 -1
- package/dist/esm/components/tags.native.js.map +1 -1
- package/dist/esm/components/timeline.mjs +1 -1
- package/dist/esm/components/timeline.mjs.map +1 -1
- package/dist/esm/components/timeline.native.js +1 -1
- package/dist/esm/components/timeline.native.js.map +1 -1
- package/dist/esm/dependsOn.native.js.map +1 -1
- package/dist/esm/desk/DeskFormPage.native.js.map +1 -1
- package/dist/esm/desk/DeskFormSidebar.native.js.map +1 -1
- package/dist/esm/desk/DeskListPage.native.js.map +1 -1
- package/dist/esm/desk/DeskShell.mjs +2 -0
- package/dist/esm/desk/DeskShell.mjs.map +1 -1
- package/dist/esm/desk/DeskShell.native.js +2 -0
- package/dist/esm/desk/DeskShell.native.js.map +1 -1
- package/dist/esm/desk/DeskSidebar.native.js.map +1 -1
- package/dist/esm/desk/awesomebar.mjs +1 -1
- package/dist/esm/desk/awesomebar.mjs.map +1 -1
- package/dist/esm/desk/awesomebar.native.js +1 -1
- package/dist/esm/desk/awesomebar.native.js.map +1 -1
- package/dist/esm/fieldOverrides.native.js.map +1 -1
- package/dist/esm/fieldRegistry.native.js.map +1 -1
- package/dist/esm/fields/Barcode/index.native.js.map +1 -1
- package/dist/esm/fields/Check/index.native.js.map +1 -1
- package/dist/esm/fields/CodeEditor/index.native.js.map +1 -1
- package/dist/esm/fields/Color/index.native.js.map +1 -1
- package/dist/esm/fields/Data/index.native.js.map +1 -1
- package/dist/esm/fields/Date/sheetKeyboard.native.js.map +1 -1
- package/dist/esm/fields/Date/types.native.js.map +1 -1
- package/dist/esm/fields/Date/wire.native.js.map +1 -1
- package/dist/esm/fields/DateRange/index.native.js.map +1 -1
- package/dist/esm/fields/Duration/index.native.js.map +1 -1
- package/dist/esm/fields/DynamicLink.native.js.map +1 -1
- package/dist/esm/fields/FileUpload.native.js.map +1 -1
- package/dist/esm/fields/FileUploadField/index.native.js.map +1 -1
- package/dist/esm/fields/Geolocation/index.native.js.map +1 -1
- package/dist/esm/fields/HTML/index.native.js.map +1 -1
- package/dist/esm/fields/Heading/index.native.js.map +1 -1
- package/dist/esm/fields/Icon/index.native.js.map +1 -1
- package/dist/esm/fields/Link.native.js.map +1 -1
- package/dist/esm/fields/LinkTree.native.js.map +1 -1
- package/dist/esm/fields/LongText/index.native.js.map +1 -1
- package/dist/esm/fields/MentionInput/index.mjs +4 -1
- package/dist/esm/fields/MentionInput/index.mjs.map +1 -1
- package/dist/esm/fields/MentionInput/index.native.js +4 -1
- package/dist/esm/fields/MentionInput/index.native.js.map +1 -1
- package/dist/esm/fields/Numeric/index.native.js.map +1 -1
- package/dist/esm/fields/OTPInput/index.native.js.map +1 -1
- package/dist/esm/fields/Password/index.native.js.map +1 -1
- package/dist/esm/fields/Phone/index.native.js.map +1 -1
- package/dist/esm/fields/Progress/index.native.js.map +1 -1
- package/dist/esm/fields/RadioGroup/index.native.js.map +1 -1
- package/dist/esm/fields/Rating/index.native.js.map +1 -1
- package/dist/esm/fields/ReadOnly/index.native.js.map +1 -1
- package/dist/esm/fields/Select/index.native.js.map +1 -1
- package/dist/esm/fields/Signature/index.native.js.map +1 -1
- package/dist/esm/fields/Slider/index.native.js.map +1 -1
- package/dist/esm/fields/SmallText/index.native.js.map +1 -1
- package/dist/esm/fields/Switch/index.native.js.map +1 -1
- package/dist/esm/fields/TableMultiSelect.native.js.map +1 -1
- package/dist/esm/fields/TextEditor/types.native.js.map +1 -1
- package/dist/esm/fields/TimePicker/index.native.js.map +1 -1
- package/dist/esm/fields/ToggleGroup/index.native.js.map +1 -1
- package/dist/esm/fields/shared/EditorPreviewPanes.native.js.map +1 -1
- package/dist/esm/filterFields.native.js.map +1 -1
- package/dist/esm/formEngine/createPrefill.native.js.map +1 -1
- package/dist/esm/formEngine/dirtyTracking.native.js.map +1 -1
- package/dist/esm/formEngine/doctypeMeta.native.js.map +1 -1
- package/dist/esm/formEngine/fetchFrom.native.js.map +1 -1
- package/dist/esm/formEngine/fieldPermissions.native.js.map +1 -1
- package/dist/esm/formEngine/formActions.native.js.map +1 -1
- package/dist/esm/formEngine/formLayout.native.js.map +1 -1
- package/dist/esm/formEngine/index.native.js.map +1 -1
- package/dist/esm/formEngine/useFormEngine.native.js.map +1 -1
- package/dist/esm/frappeEventColor.native.js.map +1 -1
- package/dist/esm/frappeStoryTarget.native.js.map +1 -1
- package/dist/esm/frappeUsers.native.js.map +1 -1
- package/dist/esm/index.mjs +2 -1
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/index.native.js +2 -1
- package/dist/esm/index.native.js.map +1 -1
- package/dist/esm/liveErrors.native.js.map +1 -1
- package/dist/esm/resolveControl.native.js.map +1 -1
- package/dist/esm/schemaForm/SchemaWizard.native.js.map +1 -1
- package/dist/esm/schemaForm/applyFieldOrder.native.js.map +1 -1
- package/dist/esm/schemaForm/index.mjs +8 -7
- package/dist/esm/schemaForm/index.mjs.map +1 -1
- package/dist/esm/schemaForm/index.native.js +8 -7
- package/dist/esm/schemaForm/index.native.js.map +1 -1
- package/dist/esm/shared/devWarn.mjs +14 -0
- package/dist/esm/shared/devWarn.mjs.map +1 -0
- package/dist/esm/shared/devWarn.native.js +16 -0
- package/dist/esm/shared/devWarn.native.js.map +1 -0
- package/dist/esm/shared/useKeyboardInset.native.js.map +1 -1
- package/dist/esm/syncError.native.js.map +1 -1
- package/dist/esm/useFrappeAggregate.mjs +15 -8
- package/dist/esm/useFrappeAggregate.mjs.map +1 -1
- package/dist/esm/useFrappeAggregate.native.js +15 -9
- package/dist/esm/useFrappeAggregate.native.js.map +1 -1
- package/dist/esm/useFrappeFacets.mjs +10 -9
- package/dist/esm/useFrappeFacets.mjs.map +1 -1
- package/dist/esm/useFrappeFacets.native.js +10 -10
- package/dist/esm/useFrappeFacets.native.js.map +1 -1
- package/dist/esm/useFrappePagination.native.js.map +1 -1
- package/dist/esm/useFrappeRows.mjs +86 -0
- package/dist/esm/useFrappeRows.mjs.map +1 -0
- package/dist/esm/useFrappeRows.native.js +99 -0
- package/dist/esm/useFrappeRows.native.js.map +1 -0
- package/dist/jsx/FrappeCalendar.mjs +20 -17
- package/dist/jsx/FrappeCalendar.mjs.map +1 -1
- package/dist/jsx/FrappeCalendar.native.js +22 -21
- package/dist/jsx/FrappeCalendar.native.js.map +1 -1
- package/dist/jsx/FrappeForm.mjs +1 -1
- package/dist/jsx/FrappeForm.mjs.map +1 -1
- package/dist/jsx/FrappeForm.native.js +1 -1
- package/dist/jsx/FrappeForm.native.js.map +1 -1
- package/dist/jsx/FrappeGantt.mjs +27 -36
- package/dist/jsx/FrappeGantt.mjs.map +1 -1
- package/dist/jsx/FrappeGantt.native.js +27 -38
- package/dist/jsx/FrappeGantt.native.js.map +1 -1
- package/dist/jsx/FrappeImageView.mjs +5 -3
- package/dist/jsx/FrappeImageView.mjs.map +1 -1
- package/dist/jsx/FrappeImageView.native.js +8 -4
- package/dist/jsx/FrappeImageView.native.js.map +1 -1
- package/dist/jsx/FrappeKPICard.mjs +27 -2
- package/dist/jsx/FrappeKPICard.mjs.map +1 -1
- package/dist/jsx/FrappeKPICard.native.js +26 -1
- package/dist/jsx/FrappeKPICard.native.js.map +1 -1
- package/dist/jsx/FrappeKanban.mjs +16 -17
- package/dist/jsx/FrappeKanban.mjs.map +1 -1
- package/dist/jsx/FrappeKanban.native.js +18 -21
- package/dist/jsx/FrappeKanban.native.js.map +1 -1
- package/dist/jsx/FrappeLikedBy.mjs +1 -1
- package/dist/jsx/FrappeLikedBy.mjs.map +1 -1
- package/dist/jsx/FrappeLikedBy.native.js +1 -1
- package/dist/jsx/FrappeLikedBy.native.js.map +1 -1
- package/dist/jsx/FrappeList.mjs +2 -2
- package/dist/jsx/FrappeList.mjs.map +1 -1
- package/dist/jsx/FrappeList.native.js +2 -2
- package/dist/jsx/FrappeList.native.js.map +1 -1
- package/dist/jsx/FrappeProvider.mjs.map +1 -1
- package/dist/jsx/FrappeProvider.native.js.map +1 -1
- package/dist/jsx/FrappeReportBuilder.mjs +2 -2
- package/dist/jsx/FrappeReportBuilder.mjs.map +1 -1
- package/dist/jsx/FrappeReportBuilder.native.js +2 -2
- package/dist/jsx/FrappeReportBuilder.native.js.map +1 -1
- package/dist/jsx/FrappeTable.mjs +36 -39
- package/dist/jsx/FrappeTable.mjs.map +1 -1
- package/dist/jsx/FrappeTable.native.js +38 -39
- package/dist/jsx/FrappeTable.native.js.map +1 -1
- package/dist/jsx/FrappeTreeView.mjs +89 -55
- package/dist/jsx/FrappeTreeView.mjs.map +1 -1
- package/dist/jsx/FrappeTreeView.native.js +129 -50
- package/dist/jsx/FrappeTreeView.native.js.map +1 -1
- package/dist/jsx/commentActions.mjs +1 -1
- package/dist/jsx/commentActions.mjs.map +1 -1
- package/dist/jsx/commentActions.native.js +1 -1
- package/dist/jsx/commentActions.native.js.map +1 -1
- package/dist/jsx/components/timeline.mjs +1 -1
- package/dist/jsx/components/timeline.mjs.map +1 -1
- package/dist/jsx/components/timeline.native.js +1 -1
- package/dist/jsx/desk/DeskShell.mjs +2 -0
- package/dist/jsx/desk/DeskShell.mjs.map +1 -1
- package/dist/jsx/desk/DeskShell.native.js +2 -0
- package/dist/jsx/desk/DeskShell.native.js.map +1 -1
- package/dist/jsx/desk/awesomebar.mjs +1 -1
- package/dist/jsx/desk/awesomebar.mjs.map +1 -1
- package/dist/jsx/desk/awesomebar.native.js +1 -1
- package/dist/jsx/desk/awesomebar.native.js.map +1 -1
- package/dist/jsx/fields/MentionInput/index.mjs +4 -1
- package/dist/jsx/fields/MentionInput/index.mjs.map +1 -1
- package/dist/jsx/fields/MentionInput/index.native.js +4 -1
- package/dist/jsx/fields/MentionInput/index.native.js.map +1 -1
- package/dist/jsx/index.js +2 -1
- package/dist/jsx/index.js.map +1 -1
- package/dist/jsx/index.mjs +2 -1
- package/dist/jsx/index.mjs.map +1 -1
- package/dist/jsx/index.native.js +4 -0
- package/dist/jsx/index.native.js.map +1 -1
- package/dist/jsx/schemaForm/index.mjs +8 -7
- package/dist/jsx/schemaForm/index.mjs.map +1 -1
- package/dist/jsx/schemaForm/index.native.js +10 -9
- package/dist/jsx/schemaForm/index.native.js.map +1 -1
- package/dist/jsx/shared/devWarn.mjs +14 -0
- package/dist/jsx/shared/devWarn.mjs.map +1 -0
- package/dist/jsx/shared/devWarn.native.js +45 -0
- package/dist/jsx/shared/devWarn.native.js.map +1 -0
- package/dist/jsx/useFrappeAggregate.mjs +15 -8
- package/dist/jsx/useFrappeAggregate.mjs.map +1 -1
- package/dist/jsx/useFrappeAggregate.native.js +15 -9
- package/dist/jsx/useFrappeAggregate.native.js.map +1 -1
- package/dist/jsx/useFrappeFacets.mjs +10 -9
- package/dist/jsx/useFrappeFacets.mjs.map +1 -1
- package/dist/jsx/useFrappeFacets.native.js +10 -10
- package/dist/jsx/useFrappeFacets.native.js.map +1 -1
- package/dist/jsx/useFrappeRows.mjs +86 -0
- package/dist/jsx/useFrappeRows.mjs.map +1 -0
- package/dist/jsx/useFrappeRows.native.js +129 -0
- package/dist/jsx/useFrappeRows.native.js.map +1 -0
- package/package.json +17 -16
- package/src/FileBrowser/FileBrowser.spec.tsx +11 -1
- package/src/FrappeCalendar.tsx +25 -22
- package/src/FrappeForm.tsx +7 -3
- package/src/FrappeGantt.tsx +29 -39
- package/src/FrappeImageView.tsx +5 -3
- package/src/FrappeKPICard.spec.tsx +128 -0
- package/src/FrappeKPICard.tsx +44 -4
- package/src/FrappeKanban.tsx +24 -17
- package/src/FrappeLikedBy.tsx +1 -1
- package/src/FrappeList.tsx +3 -2
- package/src/FrappeProvider.fixturesOnly.spec.tsx +129 -0
- package/src/FrappeProvider.tsx +16 -6
- package/src/FrappeReportBuilder.tsx +4 -2
- package/src/FrappeTable.autoHeight.spec.tsx +142 -0
- package/src/FrappeTable.rowAccumulator.spec.tsx +110 -0
- package/src/FrappeTable.tsx +75 -48
- package/src/FrappeTreeView.stories.tsx +59 -0
- package/src/FrappeTreeView.tsx +166 -56
- package/src/commentActions.ts +1 -1
- package/src/components/timeline.tsx +1 -1
- package/src/desk/DeskShell.tsx +2 -0
- package/src/desk/awesomebar.tsx +5 -2
- package/src/fields/CodeEditor/CodeEditor.spec.tsx +10 -9
- package/src/fields/Date/typeParity.spec.tsx +19 -9
- package/src/fields/Duration/Duration.spec.tsx +73 -0
- package/src/fields/MentionInput/MentionInput.spec.tsx +110 -0
- package/src/fields/MentionInput/index.tsx +4 -1
- package/src/fields/Password/index.tsx +1 -1
- package/src/fields/Progress/Progress.spec.tsx +9 -0
- package/src/fields/RadioGroup/RadioGroup.spec.tsx +93 -0
- package/src/fields/Signature/Signature.spec.tsx +105 -0
- package/src/fields/Slider/Slider.spec.tsx +73 -0
- package/src/fields/Switch/Switch.spec.tsx +65 -0
- package/src/fields/TextEditor/TextEditor.spec.tsx +2 -3
- package/src/fields/TextEditor/TextEditor.stories.tsx +31 -1
- package/src/fields/ToggleGroup/ToggleGroup.spec.tsx +110 -0
- package/src/frappeViews.designLaw.spec.ts +11 -7
- package/src/index.ts +13 -0
- package/src/schemaForm/index.tsx +6 -7
- package/src/shared/devWarn.ts +42 -0
- package/src/useFrappeAggregate.ts +17 -11
- package/src/useFrappeFacets.ts +17 -10
- package/src/useFrappeRows.spec.tsx +176 -0
- package/src/useFrappeRows.ts +293 -0
- package/types/FrappeCalendar.d.ts.map +1 -1
- package/types/FrappeForm.d.ts +6 -2
- package/types/FrappeForm.d.ts.map +1 -1
- package/types/FrappeGantt.d.ts.map +1 -1
- package/types/FrappeImageView.d.ts.map +1 -1
- package/types/FrappeKPICard.d.ts +9 -1
- package/types/FrappeKPICard.d.ts.map +1 -1
- package/types/FrappeKanban.d.ts.map +1 -1
- package/types/FrappeList.d.ts.map +1 -1
- package/types/FrappeProvider.d.ts +16 -6
- package/types/FrappeProvider.d.ts.map +1 -1
- package/types/FrappeReportBuilder.d.ts.map +1 -1
- package/types/FrappeTable.d.ts +20 -1
- package/types/FrappeTable.d.ts.map +1 -1
- package/types/FrappeTreeView.d.ts +38 -1
- package/types/FrappeTreeView.d.ts.map +1 -1
- package/types/desk/DeskShell.d.ts.map +1 -1
- package/types/desk/awesomebar.d.ts.map +1 -1
- package/types/fields/MentionInput/index.d.ts.map +1 -1
- package/types/fields/Password/index.d.ts +1 -1
- package/types/fields/Password/index.d.ts.map +1 -1
- package/types/index.d.ts +1 -0
- package/types/index.d.ts.map +1 -1
- package/types/schemaForm/index.d.ts.map +1 -1
- package/types/shared/devWarn.d.ts +23 -0
- package/types/shared/devWarn.d.ts.map +1 -0
- package/types/useFrappeAggregate.d.ts +2 -1
- package/types/useFrappeAggregate.d.ts.map +1 -1
- package/types/useFrappeFacets.d.ts +2 -1
- package/types/useFrappeFacets.d.ts.map +1 -1
- package/types/useFrappeRows.d.ts +186 -0
- package/types/useFrappeRows.d.ts.map +1 -0
|
@@ -75,10 +75,23 @@ function regionMeta(name: string, isTree: boolean) {
|
|
|
75
75
|
};
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
+
/** A chart of accounts: no parent field, no is_tree, the name is the path. */
|
|
79
|
+
function accountMeta(name: string) {
|
|
80
|
+
return {
|
|
81
|
+
name,
|
|
82
|
+
module: "QA",
|
|
83
|
+
title_field: "name",
|
|
84
|
+
is_tree: 0,
|
|
85
|
+
fields: [{ fieldname: "balance", fieldtype: "Currency", label: "Balance", in_list_view: 1 }],
|
|
86
|
+
permissions: [{ role: "All", read: 1 }],
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
78
90
|
function handleMetaRequest(url: URL): Response | null {
|
|
79
91
|
const metaMatch = url.pathname.match(/^\/api\/resource\/DocType\/(.+)$/);
|
|
80
92
|
if (!metaMatch) return null;
|
|
81
93
|
const name = decodeURIComponent(metaMatch[1]);
|
|
94
|
+
if (name === "QaAccount") return jsonResponse({ data: accountMeta(name) });
|
|
82
95
|
return jsonResponse({ data: regionMeta(name, name !== "QaFlatDoc") });
|
|
83
96
|
}
|
|
84
97
|
|
|
@@ -143,6 +156,52 @@ export const NotATree = () => (
|
|
|
143
156
|
</FrappeProvider>
|
|
144
157
|
);
|
|
145
158
|
|
|
159
|
+
interface Account {
|
|
160
|
+
name: string;
|
|
161
|
+
doctype: string;
|
|
162
|
+
balance: string;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Seven rows, and every one of them is a leaf. "Assets", "Assets:Personal"
|
|
167
|
+
* and "Assets:Personal:Bank" are not documents at all.
|
|
168
|
+
*/
|
|
169
|
+
const accountDocs: Account[] = [
|
|
170
|
+
{ name: "Assets:Personal:Bank:Checking", doctype: "QaAccount", balance: "4,120.55" },
|
|
171
|
+
{ name: "Assets:Personal:Bank:Savings", doctype: "QaAccount", balance: "18,300.00" },
|
|
172
|
+
{ name: "Assets:Personal:Cash", doctype: "QaAccount", balance: "260.00" },
|
|
173
|
+
{ name: "Expenses:Groceries", doctype: "QaAccount", balance: "612.40" },
|
|
174
|
+
{ name: "Expenses:Utilities:Power", doctype: "QaAccount", balance: "148.90" },
|
|
175
|
+
{ name: "Income:Salary", doctype: "QaAccount", balance: "9,000.00" },
|
|
176
|
+
{ name: "Liabilities:Credit Card", doctype: "QaAccount", balance: "-732.10" },
|
|
177
|
+
];
|
|
178
|
+
|
|
179
|
+
/** Colon-path doctype: is_tree is 0, there is no parent field, tree renders. */
|
|
180
|
+
export const ColonPath = () => (
|
|
181
|
+
<FrappeProvider
|
|
182
|
+
baseURL={MOCK_HOST}
|
|
183
|
+
fixtures={new InMemoryFixtureProvider({ QaAccount: accountDocs })}
|
|
184
|
+
>
|
|
185
|
+
<FrappeTreeView
|
|
186
|
+
doctype="QaAccount"
|
|
187
|
+
pathSeparator=":"
|
|
188
|
+
pathField="name"
|
|
189
|
+
valueField="balance"
|
|
190
|
+
expandByDefault
|
|
191
|
+
onNodeClick={action("onNodeClick")}
|
|
192
|
+
onPathSelect={action("onPathSelect")}
|
|
193
|
+
/>
|
|
194
|
+
</FrappeProvider>
|
|
195
|
+
);
|
|
196
|
+
ColonPath.parameters = {
|
|
197
|
+
docs: {
|
|
198
|
+
description: {
|
|
199
|
+
story:
|
|
200
|
+
"The account IS its path (Assets:Personal:Bank:Checking). Meta reports is_tree 0 and the doctype has no parent field, so the parent-field path would paint the unsupported state instead. pathSeparator synthesises the branches client-side from each row's own key. A synthesised branch carries no document: clicking it fires onPathSelect with the path and no doc, and onNodeClick stays silent. The balances hang off the leaves, which are the only real rows.",
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
};
|
|
204
|
+
|
|
146
205
|
/** Tree doctype with zero root nodes: empty message. */
|
|
147
206
|
export const EmptyTree = () => (
|
|
148
207
|
<FrappeProvider baseURL={MOCK_HOST} fixtures={new InMemoryFixtureProvider({ QaRegionEmpty: [] })}>
|
package/src/FrappeTreeView.tsx
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { useDocTypeMeta
|
|
2
|
-
import { AsyncBoundary, TreeView, type TreeNode } from "@multiplatform.one/components";
|
|
1
|
+
import { useDocTypeMeta } from "@multiplatform.one/frappe";
|
|
2
|
+
import { AsyncBoundary, EmptyState, TreeView, type TreeNode } from "@multiplatform.one/components";
|
|
3
3
|
import { TreeStructureIcon } from "@phosphor-icons/react";
|
|
4
|
-
import React, { useCallback,
|
|
4
|
+
import React, { useCallback, useMemo, useState } from "react";
|
|
5
5
|
import { useTranslation } from "react-i18next";
|
|
6
6
|
import { Text, YStack, type YStackProps } from "tamagui";
|
|
7
|
-
import { useResolvedKnobs } from "@multiplatform.one/theme";
|
|
7
|
+
import { useGlyphColor, useResolvedKnobs } from "@multiplatform.one/theme";
|
|
8
8
|
import { formCommonColors } from "@multiplatform.one/forms";
|
|
9
9
|
import { useFrappeConfig } from "./FrappeProvider";
|
|
10
10
|
import { FrappeReconnectIndicator } from "./FrappeReconnectIndicator";
|
|
11
|
-
import {
|
|
11
|
+
import { useFrappeRows } from "./useFrappeRows";
|
|
12
12
|
|
|
13
13
|
export interface FrappeTreeViewProps<TDoc extends { name: string; doctype: string }> extends Omit<
|
|
14
14
|
YStackProps,
|
|
@@ -16,11 +16,32 @@ export interface FrappeTreeViewProps<TDoc extends { name: string; doctype: strin
|
|
|
16
16
|
> {
|
|
17
17
|
doctype: string;
|
|
18
18
|
parentField?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Separator that splits {@link FrappeTreeViewProps.pathField} into levels,
|
|
21
|
+
* e.g. ":" for an account named `Assets:Personal:Bank:Checking`. Setting it
|
|
22
|
+
* switches the tree to path mode: the branches between root and leaf are
|
|
23
|
+
* synthesised from the segments, `parentField` is neither read nor fetched,
|
|
24
|
+
* and the `is_tree` gate is skipped. A doctype whose hierarchy lives in the
|
|
25
|
+
* row's own key has no parent field and the server will never report
|
|
26
|
+
* `is_tree`.
|
|
27
|
+
*/
|
|
28
|
+
pathSeparator?: string;
|
|
29
|
+
/**
|
|
30
|
+
* Row field holding the path. Path mode only; defaults to `name`. Path mode
|
|
31
|
+
* fetches `name`, this field, `valueField` and whatever `fields` adds, so a
|
|
32
|
+
* caller who needs more on the `onNodeClick` document asks for it there.
|
|
33
|
+
*/
|
|
34
|
+
pathField?: string;
|
|
19
35
|
titleField?: string;
|
|
20
36
|
valueField?: string;
|
|
21
37
|
fields?: readonly (keyof TDoc & string)[];
|
|
22
38
|
filters?: Partial<TDoc> | Array<[keyof TDoc & string, string, any]>;
|
|
23
39
|
onNodeClick?: (doc: Partial<TDoc>) => void;
|
|
40
|
+
/**
|
|
41
|
+
* Path-mode selection, fired for synthesised branches as well as documents.
|
|
42
|
+
* `doc` is present only when a document actually sits at that path.
|
|
43
|
+
*/
|
|
44
|
+
onPathSelect?: (path: string, doc?: Partial<TDoc>) => void;
|
|
24
45
|
/** Reserved for future DnD reparenting; not wired yet. */
|
|
25
46
|
enableDragDrop?: boolean;
|
|
26
47
|
/** Expand every branch on first paint (passed through to TreeView). */
|
|
@@ -88,20 +109,94 @@ export function buildTreeNodes<TDoc extends { name: string }>(
|
|
|
88
109
|
return sortDocs(roots).map(buildNode);
|
|
89
110
|
}
|
|
90
111
|
|
|
112
|
+
/** One level of the path forest, before it becomes TreeNodes. */
|
|
113
|
+
interface PathBucket<TDoc> {
|
|
114
|
+
path: string;
|
|
115
|
+
segment: string;
|
|
116
|
+
doc?: TDoc;
|
|
117
|
+
children: Map<string, PathBucket<TDoc>>;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Build a TreeNode forest from paths held on the rows themselves, e.g.
|
|
122
|
+
* `Assets:Personal:Bank:Checking` split on ":".
|
|
123
|
+
*
|
|
124
|
+
* The branches between root and leaf are not rows, so they are synthesised:
|
|
125
|
+
* a synthesised node carries no `data` and its `id` is the path prefix it
|
|
126
|
+
* stands for. A row whose path value is empty falls back to a single segment
|
|
127
|
+
* of its own name, so nothing is silently dropped.
|
|
128
|
+
*/
|
|
129
|
+
export function buildPathTreeNodes<TDoc extends { name: string }>(
|
|
130
|
+
docs: TDoc[],
|
|
131
|
+
pathField: string,
|
|
132
|
+
separator: string,
|
|
133
|
+
): TreeNode<Record<string, unknown>>[] {
|
|
134
|
+
const roots = new Map<string, PathBucket<TDoc>>();
|
|
135
|
+
|
|
136
|
+
const splitPath = (doc: TDoc): string[] => {
|
|
137
|
+
const raw = (doc as Record<string, unknown>)[pathField];
|
|
138
|
+
const value = raw == null ? "" : String(raw);
|
|
139
|
+
// An empty separator would split the value into single characters.
|
|
140
|
+
const segments = separator ? value.split(separator) : [value];
|
|
141
|
+
const cleaned = segments.map((segment) => segment.trim()).filter((segment) => segment !== "");
|
|
142
|
+
return cleaned.length > 0 ? cleaned : [doc.name];
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
for (const doc of docs) {
|
|
146
|
+
let level = roots;
|
|
147
|
+
let prefix = "";
|
|
148
|
+
let bucket: PathBucket<TDoc> | undefined;
|
|
149
|
+
for (const segment of splitPath(doc)) {
|
|
150
|
+
prefix = prefix ? `${prefix}${separator}${segment}` : segment;
|
|
151
|
+
bucket = level.get(segment);
|
|
152
|
+
if (!bucket) {
|
|
153
|
+
bucket = { path: prefix, segment, children: new Map() };
|
|
154
|
+
level.set(segment, bucket);
|
|
155
|
+
}
|
|
156
|
+
level = bucket.children;
|
|
157
|
+
}
|
|
158
|
+
// Two rows on one path: the first keeps the node rather than the last
|
|
159
|
+
// row the live query happened to deliver.
|
|
160
|
+
if (bucket && !bucket.doc) bucket.doc = doc;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const toNodes = (level: Map<string, PathBucket<TDoc>>): TreeNode<Record<string, unknown>>[] =>
|
|
164
|
+
Array.from(level.values())
|
|
165
|
+
.sort((a, b) => a.segment.localeCompare(b.segment))
|
|
166
|
+
.map((bucket) => {
|
|
167
|
+
const children = toNodes(bucket.children);
|
|
168
|
+
return {
|
|
169
|
+
id: bucket.path,
|
|
170
|
+
label: bucket.segment,
|
|
171
|
+
data: bucket.doc as unknown as Record<string, unknown> | undefined,
|
|
172
|
+
children: children.length > 0 ? children : undefined,
|
|
173
|
+
};
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
return toNodes(roots);
|
|
177
|
+
}
|
|
178
|
+
|
|
91
179
|
/**
|
|
92
180
|
* Thin Frappe wrapper around the generic TreeView component.
|
|
93
181
|
*
|
|
94
182
|
* Fetches the full DocType list via `useFrappeCollection` / `useLiveQuery`
|
|
95
183
|
* (realtime-backed), then builds a `TreeNode` forest from the parent field.
|
|
184
|
+
*
|
|
185
|
+
* Pass `pathSeparator` instead when the hierarchy lives in the row's own key
|
|
186
|
+
* (`Assets:Personal:Bank:Checking`). The branches are synthesised client-side,
|
|
187
|
+
* so no parent field and no `is_tree` are needed.
|
|
96
188
|
*/
|
|
97
189
|
export function FrappeTreeView<TDoc extends { name: string; doctype: string }>({
|
|
98
190
|
doctype,
|
|
99
191
|
parentField: parentFieldProp,
|
|
192
|
+
pathSeparator,
|
|
193
|
+
pathField,
|
|
100
194
|
titleField: titleFieldProp,
|
|
101
195
|
valueField,
|
|
102
196
|
fields,
|
|
103
197
|
filters,
|
|
104
198
|
onNodeClick,
|
|
199
|
+
onPathSelect,
|
|
105
200
|
enableDragDrop: _enableDragDrop = false,
|
|
106
201
|
expandByDefault = false,
|
|
107
202
|
display = "default",
|
|
@@ -116,6 +211,7 @@ export function FrappeTreeView<TDoc extends { name: string; doctype: string }>({
|
|
|
116
211
|
|
|
117
212
|
const config = useFrappeConfig();
|
|
118
213
|
const { knobProps } = useResolvedKnobs();
|
|
214
|
+
const emptyIconColor = useGlyphColor(formCommonColors.muted);
|
|
119
215
|
const { t } = useTranslation();
|
|
120
216
|
const [selectedId, setSelectedId] = useState<string | undefined>();
|
|
121
217
|
|
|
@@ -123,8 +219,10 @@ export function FrappeTreeView<TDoc extends { name: string; doctype: string }>({
|
|
|
123
219
|
const finalAuth = auth ?? config?.auth;
|
|
124
220
|
const finalSyncOptions = syncOptions ?? config?.syncOptions;
|
|
125
221
|
|
|
126
|
-
if (!finalBaseURL) {
|
|
127
|
-
throw new Error(
|
|
222
|
+
if (!finalBaseURL && !config?.fixtures) {
|
|
223
|
+
throw new Error(
|
|
224
|
+
"FrappeTreeView requires baseURL prop or FrappeProvider with baseURL or fixtures",
|
|
225
|
+
);
|
|
128
226
|
}
|
|
129
227
|
|
|
130
228
|
const appConfig = useMemo(
|
|
@@ -152,6 +250,8 @@ export function FrappeTreeView<TDoc extends { name: string; doctype: string }>({
|
|
|
152
250
|
|
|
153
251
|
const { meta, isLoading: isMetaLoading, error: metaError } = useDocTypeMeta(appConfig, doctype);
|
|
154
252
|
|
|
253
|
+
const isPathMode = !!pathSeparator;
|
|
254
|
+
const effectivePathField = pathField ?? "name";
|
|
155
255
|
const effectiveTitleField = titleFieldProp ?? meta?.title_field ?? "name";
|
|
156
256
|
const effectiveParentField =
|
|
157
257
|
parentFieldProp ?? `parent_${doctype.toLowerCase().replace(/ /g, "_")}`;
|
|
@@ -160,67 +260,75 @@ export function FrappeTreeView<TDoc extends { name: string; doctype: string }>({
|
|
|
160
260
|
// lets callers continue (Guest peek / offline). Successful meta always
|
|
161
261
|
// wins for the is_tree gate — including rejecting flat DocTypes that
|
|
162
262
|
// happened to pass parentField.
|
|
163
|
-
|
|
164
|
-
|
|
263
|
+
//
|
|
264
|
+
// Path mode is the exception: the hierarchy comes from the row's own key,
|
|
265
|
+
// so there is nothing for is_tree to be right about.
|
|
266
|
+
const isKnownNotTree = !isPathMode && !!meta && !meta.is_tree;
|
|
267
|
+
const canProceed = isPathMode || !!meta?.is_tree || (!!metaError && !meta && !!parentFieldProp);
|
|
165
268
|
|
|
166
269
|
const effectiveFields = useMemo(() => {
|
|
167
270
|
const fieldSet = new Set<string>(fields as string[] | undefined);
|
|
168
271
|
fieldSet.add("name");
|
|
169
|
-
|
|
170
|
-
|
|
272
|
+
if (isPathMode) {
|
|
273
|
+
// The parent field does not exist on a path-shaped doctype; asking for
|
|
274
|
+
// it would fail the read on a live bench.
|
|
275
|
+
fieldSet.add(effectivePathField);
|
|
276
|
+
} else {
|
|
277
|
+
fieldSet.add(effectiveTitleField);
|
|
278
|
+
fieldSet.add(effectiveParentField);
|
|
279
|
+
}
|
|
171
280
|
if (valueField) fieldSet.add(valueField);
|
|
172
281
|
return Array.from(fieldSet) as (keyof TDoc & string)[];
|
|
173
|
-
}, [
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
toLiveLoadError(err, {
|
|
182
|
-
permissionMessage: t("You do not have permission to view {{doctype}}", { doctype }),
|
|
183
|
-
genericMessage: t("Failed to load {{doctype}} tree", { doctype }),
|
|
184
|
-
}),
|
|
185
|
-
);
|
|
186
|
-
},
|
|
187
|
-
[doctype, t],
|
|
188
|
-
);
|
|
282
|
+
}, [
|
|
283
|
+
fields,
|
|
284
|
+
isPathMode,
|
|
285
|
+
effectivePathField,
|
|
286
|
+
effectiveTitleField,
|
|
287
|
+
effectiveParentField,
|
|
288
|
+
valueField,
|
|
289
|
+
]);
|
|
189
290
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
291
|
+
// The shared doctype read (MPO-251): collection + live query + the honest
|
|
292
|
+
// error surface a bare collection swallows — without it a 403 or a network
|
|
293
|
+
// failure renders as an empty tree. `limit: null` keeps the tree on the
|
|
294
|
+
// bench's own page length, as it was before the hook existed.
|
|
295
|
+
const rowsMessages = useMemo(
|
|
296
|
+
() => ({
|
|
297
|
+
permission: t("You do not have permission to view {{doctype}}", { doctype }),
|
|
298
|
+
generic: t("Failed to load {{doctype}} tree", { doctype }),
|
|
299
|
+
}),
|
|
300
|
+
[doctype, t],
|
|
194
301
|
);
|
|
195
|
-
|
|
196
|
-
// A new collection (doctype/filter change) starts with a clean slate.
|
|
197
|
-
useEffect(() => {
|
|
198
|
-
setSyncError(null);
|
|
199
|
-
}, [collection]);
|
|
200
|
-
|
|
201
302
|
const {
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
} =
|
|
206
|
-
|
|
207
|
-
|
|
303
|
+
rows: docs,
|
|
304
|
+
isQueryLoading: isDataLoading,
|
|
305
|
+
error: queryError,
|
|
306
|
+
} = useFrappeRows<TDoc>(appConfig, doctype, {
|
|
307
|
+
fields: effectiveFields,
|
|
308
|
+
filters,
|
|
309
|
+
limit: null,
|
|
310
|
+
enabled: canProceed && !isKnownNotTree,
|
|
311
|
+
messages: rowsMessages,
|
|
312
|
+
});
|
|
208
313
|
|
|
209
314
|
const treeNodes = useMemo(
|
|
210
|
-
() =>
|
|
211
|
-
|
|
315
|
+
() =>
|
|
316
|
+
pathSeparator
|
|
317
|
+
? buildPathTreeNodes(docs, effectivePathField, pathSeparator)
|
|
318
|
+
: buildTreeNodes(docs, effectiveParentField, effectiveTitleField),
|
|
319
|
+
[docs, pathSeparator, effectivePathField, effectiveParentField, effectiveTitleField],
|
|
212
320
|
);
|
|
213
321
|
|
|
214
|
-
const queryError =
|
|
215
|
-
syncError ?? (isError ? new Error(t("Failed to load {{doctype}} tree", { doctype })) : null);
|
|
216
|
-
|
|
217
322
|
const handleNodeSelect = useCallback(
|
|
218
323
|
(node: TreeNode<Record<string, unknown>>) => {
|
|
219
324
|
setSelectedId(node.id);
|
|
325
|
+
if (isPathMode) onPathSelect?.(node.id, node.data as Partial<TDoc> | undefined);
|
|
326
|
+
// A synthesised branch is not a document, so onNodeClick stays silent
|
|
327
|
+
// rather than handing a caller a name that would 404 on open.
|
|
220
328
|
if (!node.data) return;
|
|
221
329
|
onNodeClick?.(node.data as Partial<TDoc>);
|
|
222
330
|
},
|
|
223
|
-
[onNodeClick],
|
|
331
|
+
[isPathMode, onNodeClick, onPathSelect],
|
|
224
332
|
);
|
|
225
333
|
|
|
226
334
|
if (isMetaLoading) {
|
|
@@ -228,7 +336,7 @@ export function FrappeTreeView<TDoc extends { name: string; doctype: string }>({
|
|
|
228
336
|
return <AsyncBoundary layout="list" loading {...stackProps} />;
|
|
229
337
|
}
|
|
230
338
|
|
|
231
|
-
if (metaError && !meta && !parentFieldProp) {
|
|
339
|
+
if (metaError && !meta && !parentFieldProp && !isPathMode) {
|
|
232
340
|
if (error) return <>{error(metaError)}</>;
|
|
233
341
|
return (
|
|
234
342
|
<AsyncBoundary
|
|
@@ -243,12 +351,14 @@ export function FrappeTreeView<TDoc extends { name: string; doctype: string }>({
|
|
|
243
351
|
if (isKnownNotTree) {
|
|
244
352
|
const iconSize = Number(knobProps.controlIcon?.width) || 16;
|
|
245
353
|
return (
|
|
246
|
-
<
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
354
|
+
<EmptyState
|
|
355
|
+
title={t("This DocType does not support tree view")}
|
|
356
|
+
description={t(
|
|
357
|
+
"Pass pathSeparator and pathField to build the hierarchy from a path-shaped value on each row. parentField alone does not, because a flat DocType has no parent field.",
|
|
358
|
+
)}
|
|
359
|
+
icon={<TreeStructureIcon size={iconSize * 2} color={emptyIconColor} />}
|
|
360
|
+
{...stackProps}
|
|
361
|
+
/>
|
|
252
362
|
);
|
|
253
363
|
}
|
|
254
364
|
|
package/src/commentActions.ts
CHANGED
|
@@ -67,7 +67,7 @@ export function useFrappeCommentActions(
|
|
|
67
67
|
): FrappeCommentActions {
|
|
68
68
|
return useMemo(() => {
|
|
69
69
|
const app =
|
|
70
|
-
config && !config.fixtures
|
|
70
|
+
config?.baseURL && !config.fixtures
|
|
71
71
|
? new FrappeApp(config.baseURL, (config.auth ?? {}) as Record<string, unknown>)
|
|
72
72
|
: undefined;
|
|
73
73
|
|
|
@@ -208,7 +208,7 @@ export function Timeline({
|
|
|
208
208
|
|
|
209
209
|
const resolveSessionUser = useCallback(async (): Promise<string> => {
|
|
210
210
|
if (sessionUserRef.current) return sessionUserRef.current;
|
|
211
|
-
if (config && !config.fixtures) {
|
|
211
|
+
if (config?.baseURL && !config.fixtures) {
|
|
212
212
|
const app = new FrappeApp(config.baseURL, (config.auth ?? {}) as Record<string, unknown>);
|
|
213
213
|
sessionUserRef.current = await app.auth().getLoggedInUser();
|
|
214
214
|
} else {
|
package/src/desk/DeskShell.tsx
CHANGED
|
@@ -209,6 +209,7 @@ export function DeskShell({
|
|
|
209
209
|
{showSidebar && !isNarrow && (
|
|
210
210
|
<YStack
|
|
211
211
|
role="navigation"
|
|
212
|
+
aria-label={t("Desk navigation")}
|
|
212
213
|
id="desk-sidebar"
|
|
213
214
|
flexShrink={0}
|
|
214
215
|
data-testid="desk-sidebar-rail"
|
|
@@ -234,6 +235,7 @@ export function DeskShell({
|
|
|
234
235
|
/>
|
|
235
236
|
<YStack
|
|
236
237
|
role="navigation"
|
|
238
|
+
aria-label={t("Desk navigation")}
|
|
237
239
|
id="desk-sidebar"
|
|
238
240
|
position="absolute"
|
|
239
241
|
top={0}
|
package/src/desk/awesomebar.tsx
CHANGED
|
@@ -101,7 +101,10 @@ async function addRecentSearch(query: string): Promise<void> {
|
|
|
101
101
|
// Helpers
|
|
102
102
|
// ---------------------------------------------------------------------------
|
|
103
103
|
|
|
104
|
-
function createHttpClientFromConfig(config:
|
|
104
|
+
function createHttpClientFromConfig(config: {
|
|
105
|
+
baseURL: string;
|
|
106
|
+
auth?: FrappeConnectionConfig["auth"];
|
|
107
|
+
}): HttpClient {
|
|
105
108
|
return new HttpClient(
|
|
106
109
|
config.baseURL,
|
|
107
110
|
config.auth as Parameters<typeof HttpClient.prototype.updateAuth>[0],
|
|
@@ -168,7 +171,7 @@ export function Awesomebar({ onNavigate, basePath = "/desk", ...stackProps }: Aw
|
|
|
168
171
|
// HttpClient ref for document search
|
|
169
172
|
const httpRef = useRef<HttpClient | undefined>(undefined);
|
|
170
173
|
const configKeyRef = useRef("");
|
|
171
|
-
if (config) {
|
|
174
|
+
if (config?.baseURL) {
|
|
172
175
|
const key = `${config.baseURL}:${config.auth?.token ?? ""}`;
|
|
173
176
|
if (key !== configKeyRef.current) {
|
|
174
177
|
httpRef.current = createHttpClientFromConfig({
|
|
@@ -29,12 +29,10 @@ function readCodeEditorSource() {
|
|
|
29
29
|
return readFileSync(join(codeEditorDir(), "index.tsx"), "utf8");
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
function readCodeEditorStories() {
|
|
33
|
-
return readFileSync(join(codeEditorDir(), "CodeEditor.stories.tsx"), "utf8");
|
|
34
|
-
}
|
|
35
|
-
|
|
36
32
|
afterEach(cleanup);
|
|
37
33
|
|
|
34
|
+
vi.mock("@multiplatform.one/storybook", () => ({ action: () => vi.fn() }));
|
|
35
|
+
|
|
38
36
|
describe("frappeCodeLanguage (stock ControlCode language_map)", () => {
|
|
39
37
|
it("maps stock Frappe options case-insensitively", () => {
|
|
40
38
|
expect(frappeCodeLanguage("Python")).toBe("python");
|
|
@@ -102,11 +100,14 @@ describe("CodeEditorField (standalone)", () => {
|
|
|
102
100
|
expect(result.getTextArea()).toBeNull();
|
|
103
101
|
});
|
|
104
102
|
|
|
105
|
-
it("ships CSF3 story objects so native gallery keeps args (KS blank-pane)", () => {
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
103
|
+
it("ships CSF3 story objects so native gallery keeps args (KS blank-pane)", async () => {
|
|
104
|
+
const { Main, Json, ReadOnly } = await import("./CodeEditor.stories");
|
|
105
|
+
for (const story of [Main, Json, ReadOnly]) {
|
|
106
|
+
expect(typeof story).toBe("object");
|
|
107
|
+
expect(story.render).toBeTypeOf("function");
|
|
108
|
+
expect(story.args.value?.trim().length).toBeGreaterThan(0);
|
|
109
|
+
}
|
|
110
|
+
expect(ReadOnly.args.readOnly).toBe(true);
|
|
110
111
|
});
|
|
111
112
|
|
|
112
113
|
it("forwards knob containerRadius onto the source textarea (F14)", () => {
|
|
@@ -168,6 +168,14 @@ async function pressText(root: ParentNode, text: string) {
|
|
|
168
168
|
});
|
|
169
169
|
}
|
|
170
170
|
|
|
171
|
+
async function selectPeriod(period: "AM" | "PM") {
|
|
172
|
+
const wheel = document.querySelector('[role="slider"][aria-label="AM/PM"]');
|
|
173
|
+
expect(wheel).toBeTruthy();
|
|
174
|
+
await act(async () => {
|
|
175
|
+
fireEvent.keyDown(wheel!, { key: period === "AM" ? "Home" : "End" });
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
|
|
171
179
|
beforeEach(() => {
|
|
172
180
|
// Both halves portal their surfaces, so a previous case's chrome would
|
|
173
181
|
// otherwise still be in the document when the next one queries it.
|
|
@@ -194,7 +202,7 @@ describe("web Date field — type=date keeps the calendar", () => {
|
|
|
194
202
|
await openPanel(triggerInput(result.container)!);
|
|
195
203
|
expect(panelText()).toContain("Su");
|
|
196
204
|
// A date-only control offers no clock.
|
|
197
|
-
expect(
|
|
205
|
+
expect(document.querySelector('[role="slider"][aria-label="Hour"]')).toBeNull();
|
|
198
206
|
|
|
199
207
|
await waitFor(() => expect(dayCell(15)).toBeTruthy(), { timeout: 30_000 });
|
|
200
208
|
await act(async () => {
|
|
@@ -226,8 +234,8 @@ describe("web Date field — type=time renders a time control", () => {
|
|
|
226
234
|
);
|
|
227
235
|
await openPanel(triggerInput(result.container)!);
|
|
228
236
|
const text = panelText();
|
|
229
|
-
expect(
|
|
230
|
-
expect(
|
|
237
|
+
expect(document.querySelector('[role="slider"][aria-label="Hour"]')).toBeTruthy();
|
|
238
|
+
expect(document.querySelector('[role="slider"][aria-label="Min"]')).toBeTruthy();
|
|
231
239
|
expect(text).not.toContain("Su");
|
|
232
240
|
});
|
|
233
241
|
|
|
@@ -239,11 +247,11 @@ describe("web Date field — type=time renders a time control", () => {
|
|
|
239
247
|
);
|
|
240
248
|
await openPanel(triggerInput(result.container)!);
|
|
241
249
|
// 9:00 AM → PM is a deterministic edit with no wheel animation in it.
|
|
242
|
-
await
|
|
250
|
+
await selectPeriod("PM");
|
|
243
251
|
|
|
244
252
|
// The control speaks `HH:mm`; the field re-serializes through the shared
|
|
245
253
|
// wire body, so what persists carries seconds like every other writer.
|
|
246
|
-
expect(onChange).toHaveBeenCalledWith("21:00:00");
|
|
254
|
+
await waitFor(() => expect(onChange).toHaveBeenCalledWith("21:00:00"));
|
|
247
255
|
});
|
|
248
256
|
|
|
249
257
|
it("adopts a seconds-less HH:mm value too (what Frappe hands a fresh field)", async () => {
|
|
@@ -292,7 +300,9 @@ describe("web Date field — type=datetime renders date AND time", () => {
|
|
|
292
300
|
await openPanel(triggerInput(result.container)!);
|
|
293
301
|
expect(panelText()).toContain("Su");
|
|
294
302
|
await pressText(document.body, "Time");
|
|
295
|
-
await waitFor(() =>
|
|
303
|
+
await waitFor(() =>
|
|
304
|
+
expect(document.querySelector('[role="slider"][aria-label="Hour"]')).toBeTruthy(),
|
|
305
|
+
);
|
|
296
306
|
});
|
|
297
307
|
|
|
298
308
|
it("a day change keeps the existing time — and the picker, not the serializer, keeps it", async () => {
|
|
@@ -330,8 +340,8 @@ describe("web Date field — type=datetime renders date AND time", () => {
|
|
|
330
340
|
await pressText(document.body, "Time");
|
|
331
341
|
// 2:30 PM → AM. Carrying `previousValue` here would have pinned the old
|
|
332
342
|
// 14:30 back over the edit the user just made.
|
|
333
|
-
await
|
|
334
|
-
expect(onChange).toHaveBeenLastCalledWith("2026-03-05 02:30:00");
|
|
343
|
+
await selectPeriod("AM");
|
|
344
|
+
await waitFor(() => expect(onChange).toHaveBeenLastCalledWith("2026-03-05 02:30:00"));
|
|
335
345
|
});
|
|
336
346
|
});
|
|
337
347
|
|
|
@@ -412,7 +422,7 @@ describe("web and native persist byte-identical wire strings", () => {
|
|
|
412
422
|
|
|
413
423
|
it("time — moving 9:00 AM to 9:00 PM writes the same string on both halves", async () => {
|
|
414
424
|
const web = await webPick("time", "09:00:00", async () => {
|
|
415
|
-
await
|
|
425
|
+
await selectPeriod("PM");
|
|
416
426
|
});
|
|
417
427
|
cleanup();
|
|
418
428
|
mockState.picks.length = 0;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Duration adapter contract specs. The Frappe Duration field is seconds in,
|
|
3
|
+
* seconds out; this file only pins that the hop to forms Duration keeps
|
|
4
|
+
* value / onChange / disabled / label / error. The segment math lives in
|
|
5
|
+
* the forms spec.
|
|
6
|
+
*/
|
|
7
|
+
import { renderWithProviders } from "@multiplatform.one/test-utils";
|
|
8
|
+
import { cleanup, fireEvent } from "@testing-library/react";
|
|
9
|
+
import type React from "react";
|
|
10
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
11
|
+
import { getFieldComponent, type FieldRenderProps } from "../../fieldRegistry";
|
|
12
|
+
import { Duration } from ".";
|
|
13
|
+
|
|
14
|
+
afterEach(cleanup);
|
|
15
|
+
|
|
16
|
+
const hours = (container: HTMLElement) =>
|
|
17
|
+
container.querySelector('input[aria-label="Hours"]') as HTMLInputElement | null;
|
|
18
|
+
|
|
19
|
+
describe("Duration composes the forms Duration", () => {
|
|
20
|
+
it("paints a controlled seconds value into the hours segment", () => {
|
|
21
|
+
const result = renderWithProviders(<Duration label="Time" value={7200} onChange={() => {}} />);
|
|
22
|
+
expect(hours(result.container)?.value ?? hours(result.container)?.getAttribute("value")).toBe(
|
|
23
|
+
"2",
|
|
24
|
+
);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("emits seconds through canonical onChange", () => {
|
|
28
|
+
const onChange = vi.fn();
|
|
29
|
+
const result = renderWithProviders(<Duration label="Time" value={3600} onChange={onChange} />);
|
|
30
|
+
fireEvent.keyDown(hours(result.container) as HTMLInputElement, { key: "ArrowUp" });
|
|
31
|
+
expect(onChange).toHaveBeenCalledWith(7200);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("forwards disabled onto the hours segment", () => {
|
|
35
|
+
const result = renderWithProviders(
|
|
36
|
+
<Duration label="Time" value={3600} disabled onChange={() => {}} />,
|
|
37
|
+
);
|
|
38
|
+
const el = hours(result.container);
|
|
39
|
+
expect(el?.getAttribute("aria-disabled") === "true" || el?.disabled).toBe(true);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("forwards readOnly onto the hours segment", () => {
|
|
43
|
+
const result = renderWithProviders(
|
|
44
|
+
<Duration label="Time" value={3600} readOnly onChange={() => {}} />,
|
|
45
|
+
);
|
|
46
|
+
const el = result.container.querySelector("input");
|
|
47
|
+
expect(el?.getAttribute("aria-readonly") === "true" || el?.readOnly).toBe(true);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("shows the label and error", () => {
|
|
51
|
+
const result = renderWithProviders(
|
|
52
|
+
<Duration label="Time" value={3600} error="not a duration" onChange={() => {}} />,
|
|
53
|
+
);
|
|
54
|
+
expect(result.container.textContent).toContain("Time");
|
|
55
|
+
expect(result.container.textContent).toContain("not a duration");
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
describe("registry resolution (Duration fieldtype)", () => {
|
|
60
|
+
function propsFor(overrides: Partial<FieldRenderProps> = {}): FieldRenderProps {
|
|
61
|
+
return { value: "", onChange: () => {}, ...overrides };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
it("resolves 'Duration' to the Duration adapter and coerces a non-number to 0", () => {
|
|
65
|
+
const Component = getFieldComponent("Duration")!;
|
|
66
|
+
const element = (Component as Function)(
|
|
67
|
+
propsFor({ value: "nope", label: "Time" }),
|
|
68
|
+
) as React.ReactElement<Record<string, unknown>>;
|
|
69
|
+
expect(element.type).toBe(Duration);
|
|
70
|
+
expect(element.props.value).toBe(0);
|
|
71
|
+
expect(element.props.label).toBe("Time");
|
|
72
|
+
});
|
|
73
|
+
});
|