@multiplatform.one/frappe-ui 6.7.0 → 7.1.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/FrappeAssignment.cjs +2 -0
- package/dist/cjs/FrappeAssignment.native.js +2 -0
- package/dist/cjs/FrappeAssignment.native.js.map +1 -1
- package/dist/cjs/FrappeCalendar.cjs +5 -7
- package/dist/cjs/FrappeCalendar.native.js +5 -9
- package/dist/cjs/FrappeCalendar.native.js.map +1 -1
- package/dist/cjs/FrappeChartCard.cjs +2 -0
- package/dist/cjs/FrappeChartCard.native.js +2 -0
- package/dist/cjs/FrappeChartCard.native.js.map +1 -1
- package/dist/cjs/FrappeDashboard.cjs +11 -22
- package/dist/cjs/FrappeDashboard.native.js +20 -22
- package/dist/cjs/FrappeDashboard.native.js.map +1 -1
- package/dist/cjs/FrappeForm.cjs +3 -1
- package/dist/cjs/FrappeForm.native.js +3 -1
- package/dist/cjs/FrappeForm.native.js.map +1 -1
- package/dist/cjs/FrappeGantt.cjs +5 -7
- package/dist/cjs/FrappeGantt.native.js +5 -9
- package/dist/cjs/FrappeGantt.native.js.map +1 -1
- package/dist/cjs/FrappeImageView.cjs +3 -1
- package/dist/cjs/FrappeImageView.native.js +3 -1
- package/dist/cjs/FrappeImageView.native.js.map +1 -1
- package/dist/cjs/FrappeKPICard.cjs +2 -0
- package/dist/cjs/FrappeKPICard.native.js +2 -0
- package/dist/cjs/FrappeKPICard.native.js.map +1 -1
- package/dist/cjs/FrappeKanban.cjs +3 -1
- package/dist/cjs/FrappeKanban.native.js +3 -1
- package/dist/cjs/FrappeKanban.native.js.map +1 -1
- package/dist/cjs/FrappeLikedBy.cjs +2 -0
- package/dist/cjs/FrappeLikedBy.native.js +2 -0
- package/dist/cjs/FrappeLikedBy.native.js.map +1 -1
- package/dist/cjs/FrappeList.cjs +3 -1
- package/dist/cjs/FrappeList.native.js +3 -1
- package/dist/cjs/FrappeList.native.js.map +1 -1
- package/dist/cjs/FrappeListView.cjs +6 -12
- package/dist/cjs/FrappeListView.native.js +13 -12
- package/dist/cjs/FrappeListView.native.js.map +1 -1
- package/dist/cjs/FrappeProvider.cjs +5 -1
- package/dist/cjs/FrappeProvider.native.js +5 -1
- package/dist/cjs/FrappeProvider.native.js.map +1 -1
- package/dist/cjs/FrappeReportBuilder.cjs +2 -0
- package/dist/cjs/FrappeReportBuilder.native.js +2 -0
- package/dist/cjs/FrappeReportBuilder.native.js.map +1 -1
- package/dist/cjs/FrappeReportView.cjs +23 -45
- package/dist/cjs/FrappeReportView.native.js +35 -50
- package/dist/cjs/FrappeReportView.native.js.map +1 -1
- package/dist/cjs/FrappeTable.cjs +105 -31
- package/dist/cjs/FrappeTable.native.js +140 -42
- package/dist/cjs/FrappeTable.native.js.map +1 -1
- package/dist/cjs/FrappeTreeView.cjs +3 -1
- package/dist/cjs/FrappeTreeView.native.js +3 -1
- package/dist/cjs/FrappeTreeView.native.js.map +1 -1
- package/dist/cjs/SyncErrorNotice.cjs +76 -0
- package/dist/cjs/SyncErrorNotice.native.js +92 -0
- package/dist/cjs/SyncErrorNotice.native.js.map +1 -0
- package/dist/cjs/commentActions.cjs +120 -0
- package/dist/cjs/commentActions.native.js +129 -0
- package/dist/cjs/commentActions.native.js.map +1 -0
- package/dist/cjs/components/attachments.cjs +2 -0
- package/dist/cjs/components/attachments.native.js +2 -0
- package/dist/cjs/components/attachments.native.js.map +1 -1
- package/dist/cjs/components/tags.cjs +5 -0
- package/dist/cjs/components/tags.native.js +5 -0
- package/dist/cjs/components/tags.native.js.map +1 -1
- package/dist/cjs/components/timeline.cjs +29 -2
- package/dist/cjs/components/timeline.native.js +33 -2
- package/dist/cjs/components/timeline.native.js.map +1 -1
- package/dist/cjs/desk/DeskFormSidebar.cjs +2 -0
- package/dist/cjs/desk/DeskFormSidebar.native.js +2 -0
- package/dist/cjs/desk/DeskFormSidebar.native.js.map +1 -1
- package/dist/cjs/fieldRegistry.cjs +2 -0
- package/dist/cjs/fieldRegistry.native.js +2 -0
- package/dist/cjs/fieldRegistry.native.js.map +1 -1
- package/dist/cjs/fields/Date/index.cjs +71 -7
- package/dist/cjs/fields/Date/index.native.js +325 -37
- package/dist/cjs/fields/Date/index.native.js.map +1 -1
- package/dist/cjs/fields/Date/types.cjs +18 -0
- package/dist/cjs/fields/Date/types.native.js +21 -0
- package/dist/cjs/fields/Date/types.native.js.map +1 -0
- package/dist/cjs/fields/Date/wire.cjs +4 -8
- package/dist/cjs/fields/Date/wire.native.js +4 -11
- package/dist/cjs/fields/Date/wire.native.js.map +1 -1
- package/dist/cjs/fields/DynamicLink.cjs +2 -0
- package/dist/cjs/fields/DynamicLink.native.js +2 -0
- package/dist/cjs/fields/DynamicLink.native.js.map +1 -1
- package/dist/cjs/fields/FileUpload.cjs +2 -0
- package/dist/cjs/fields/FileUpload.native.js +2 -0
- package/dist/cjs/fields/FileUpload.native.js.map +1 -1
- package/dist/cjs/fields/TableMultiSelect.cjs +6 -1
- package/dist/cjs/fields/TableMultiSelect.native.js +6 -1
- package/dist/cjs/fields/TableMultiSelect.native.js.map +1 -1
- package/dist/cjs/frappeStoryTarget.cjs +88 -0
- package/dist/cjs/frappeStoryTarget.native.js +100 -0
- package/dist/cjs/frappeStoryTarget.native.js.map +1 -1
- package/dist/cjs/frappeUsers.cjs +2 -0
- package/dist/cjs/frappeUsers.native.js +2 -0
- package/dist/cjs/frappeUsers.native.js.map +1 -1
- package/dist/cjs/index.cjs +17 -4
- package/dist/cjs/index.native.js +17 -4
- package/dist/cjs/index.native.js.map +1 -1
- package/dist/cjs/liveErrors.cjs +5 -1
- package/dist/cjs/liveErrors.native.js +5 -1
- package/dist/cjs/liveErrors.native.js.map +1 -1
- package/dist/cjs/useFrappeAggregate.cjs +13 -1
- package/dist/cjs/useFrappeAggregate.native.js +14 -1
- package/dist/cjs/useFrappeAggregate.native.js.map +1 -1
- package/dist/cjs/useFrappeFacets.cjs +329 -0
- package/dist/cjs/useFrappeFacets.native.js +632 -0
- package/dist/cjs/useFrappeFacets.native.js.map +1 -0
- package/dist/esm/FrappeAssignment.mjs +2 -0
- package/dist/esm/FrappeAssignment.mjs.map +1 -1
- package/dist/esm/FrappeAssignment.native.js +2 -0
- package/dist/esm/FrappeAssignment.native.js.map +1 -1
- package/dist/esm/FrappeCalendar.mjs +6 -8
- package/dist/esm/FrappeCalendar.mjs.map +1 -1
- package/dist/esm/FrappeCalendar.native.js +6 -10
- package/dist/esm/FrappeCalendar.native.js.map +1 -1
- package/dist/esm/FrappeChartCard.mjs +2 -0
- package/dist/esm/FrappeChartCard.mjs.map +1 -1
- package/dist/esm/FrappeChartCard.native.js +2 -0
- package/dist/esm/FrappeChartCard.native.js.map +1 -1
- package/dist/esm/FrappeDashboard.mjs +12 -19
- package/dist/esm/FrappeDashboard.mjs.map +1 -1
- package/dist/esm/FrappeDashboard.native.js +23 -21
- package/dist/esm/FrappeDashboard.native.js.map +1 -1
- package/dist/esm/FrappeForm.mjs +3 -1
- package/dist/esm/FrappeForm.mjs.map +1 -1
- package/dist/esm/FrappeForm.native.js +3 -1
- package/dist/esm/FrappeForm.native.js.map +1 -1
- package/dist/esm/FrappeGantt.mjs +6 -8
- package/dist/esm/FrappeGantt.mjs.map +1 -1
- package/dist/esm/FrappeGantt.native.js +6 -10
- package/dist/esm/FrappeGantt.native.js.map +1 -1
- package/dist/esm/FrappeImageView.mjs +3 -1
- package/dist/esm/FrappeImageView.mjs.map +1 -1
- package/dist/esm/FrappeImageView.native.js +3 -1
- package/dist/esm/FrappeImageView.native.js.map +1 -1
- package/dist/esm/FrappeKPICard.mjs +2 -0
- package/dist/esm/FrappeKPICard.mjs.map +1 -1
- package/dist/esm/FrappeKPICard.native.js +2 -0
- package/dist/esm/FrappeKPICard.native.js.map +1 -1
- package/dist/esm/FrappeKanban.mjs +3 -1
- package/dist/esm/FrappeKanban.mjs.map +1 -1
- package/dist/esm/FrappeKanban.native.js +3 -1
- package/dist/esm/FrappeKanban.native.js.map +1 -1
- package/dist/esm/FrappeLikedBy.mjs +2 -0
- package/dist/esm/FrappeLikedBy.mjs.map +1 -1
- package/dist/esm/FrappeLikedBy.native.js +2 -0
- package/dist/esm/FrappeLikedBy.native.js.map +1 -1
- package/dist/esm/FrappeList.mjs +3 -1
- package/dist/esm/FrappeList.mjs.map +1 -1
- package/dist/esm/FrappeList.native.js +3 -1
- package/dist/esm/FrappeList.native.js.map +1 -1
- package/dist/esm/FrappeListView.mjs +7 -13
- package/dist/esm/FrappeListView.mjs.map +1 -1
- package/dist/esm/FrappeListView.native.js +15 -14
- package/dist/esm/FrappeListView.native.js.map +1 -1
- package/dist/esm/FrappeProvider.mjs +5 -1
- package/dist/esm/FrappeProvider.mjs.map +1 -1
- package/dist/esm/FrappeProvider.native.js +5 -1
- package/dist/esm/FrappeProvider.native.js.map +1 -1
- package/dist/esm/FrappeReportBuilder.mjs +2 -0
- package/dist/esm/FrappeReportBuilder.mjs.map +1 -1
- package/dist/esm/FrappeReportBuilder.native.js +2 -0
- package/dist/esm/FrappeReportBuilder.native.js.map +1 -1
- package/dist/esm/FrappeReportView.mjs +26 -49
- package/dist/esm/FrappeReportView.mjs.map +1 -1
- package/dist/esm/FrappeReportView.native.js +38 -54
- package/dist/esm/FrappeReportView.native.js.map +1 -1
- package/dist/esm/FrappeTable.mjs +107 -33
- package/dist/esm/FrappeTable.mjs.map +1 -1
- package/dist/esm/FrappeTable.native.js +142 -44
- package/dist/esm/FrappeTable.native.js.map +1 -1
- package/dist/esm/FrappeTreeView.mjs +3 -1
- package/dist/esm/FrappeTreeView.mjs.map +1 -1
- package/dist/esm/FrappeTreeView.native.js +3 -1
- package/dist/esm/FrappeTreeView.native.js.map +1 -1
- package/dist/esm/SyncErrorNotice.mjs +51 -0
- package/dist/esm/SyncErrorNotice.mjs.map +1 -0
- package/dist/esm/SyncErrorNotice.native.js +53 -0
- package/dist/esm/SyncErrorNotice.native.js.map +1 -0
- package/dist/esm/commentActions.mjs +95 -0
- package/dist/esm/commentActions.mjs.map +1 -0
- package/dist/esm/commentActions.native.js +101 -0
- package/dist/esm/commentActions.native.js.map +1 -0
- package/dist/esm/components/attachments.mjs +2 -0
- package/dist/esm/components/attachments.mjs.map +1 -1
- package/dist/esm/components/attachments.native.js +2 -0
- package/dist/esm/components/attachments.native.js.map +1 -1
- package/dist/esm/components/tags.mjs +5 -0
- package/dist/esm/components/tags.mjs.map +1 -1
- package/dist/esm/components/tags.native.js +5 -0
- package/dist/esm/components/tags.native.js.map +1 -1
- package/dist/esm/components/timeline.mjs +31 -4
- package/dist/esm/components/timeline.mjs.map +1 -1
- package/dist/esm/components/timeline.native.js +35 -4
- package/dist/esm/components/timeline.native.js.map +1 -1
- package/dist/esm/desk/DeskFormSidebar.mjs +2 -0
- package/dist/esm/desk/DeskFormSidebar.mjs.map +1 -1
- package/dist/esm/desk/DeskFormSidebar.native.js +2 -0
- package/dist/esm/desk/DeskFormSidebar.native.js.map +1 -1
- package/dist/esm/fieldRegistry.mjs +2 -0
- package/dist/esm/fieldRegistry.mjs.map +1 -1
- package/dist/esm/fieldRegistry.native.js +2 -0
- package/dist/esm/fieldRegistry.native.js.map +1 -1
- package/dist/esm/fields/Date/index.mjs +73 -9
- package/dist/esm/fields/Date/index.mjs.map +1 -1
- package/dist/esm/fields/Date/index.native.js +315 -38
- package/dist/esm/fields/Date/index.native.js.map +1 -1
- package/dist/esm/fields/Date/types.mjs +2 -0
- package/dist/esm/fields/Date/types.mjs.map +1 -0
- package/dist/esm/fields/Date/types.native.js +2 -0
- package/dist/esm/fields/Date/types.native.js.map +1 -0
- package/dist/esm/fields/Date/wire.mjs +4 -8
- package/dist/esm/fields/Date/wire.mjs.map +1 -1
- package/dist/esm/fields/Date/wire.native.js +4 -11
- package/dist/esm/fields/Date/wire.native.js.map +1 -1
- package/dist/esm/fields/DynamicLink.mjs +2 -0
- package/dist/esm/fields/DynamicLink.mjs.map +1 -1
- package/dist/esm/fields/DynamicLink.native.js +2 -0
- package/dist/esm/fields/DynamicLink.native.js.map +1 -1
- package/dist/esm/fields/FileUpload.mjs +2 -0
- package/dist/esm/fields/FileUpload.mjs.map +1 -1
- package/dist/esm/fields/FileUpload.native.js +2 -0
- package/dist/esm/fields/FileUpload.native.js.map +1 -1
- package/dist/esm/fields/TableMultiSelect.mjs +7 -2
- package/dist/esm/fields/TableMultiSelect.mjs.map +1 -1
- package/dist/esm/fields/TableMultiSelect.native.js +7 -2
- package/dist/esm/fields/TableMultiSelect.native.js.map +1 -1
- package/dist/esm/frappeStoryTarget.mjs +88 -2
- package/dist/esm/frappeStoryTarget.mjs.map +1 -1
- package/dist/esm/frappeStoryTarget.native.js +100 -2
- package/dist/esm/frappeStoryTarget.native.js.map +1 -1
- package/dist/esm/frappeUsers.mjs +2 -0
- package/dist/esm/frappeUsers.mjs.map +1 -1
- package/dist/esm/frappeUsers.native.js +2 -0
- package/dist/esm/frappeUsers.native.js.map +1 -1
- package/dist/esm/index.mjs +7 -3
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/index.native.js +7 -3
- package/dist/esm/index.native.js.map +1 -1
- package/dist/esm/liveErrors.mjs +5 -1
- package/dist/esm/liveErrors.mjs.map +1 -1
- package/dist/esm/liveErrors.native.js +5 -1
- package/dist/esm/liveErrors.native.js.map +1 -1
- package/dist/esm/useFrappeAggregate.mjs +13 -1
- package/dist/esm/useFrappeAggregate.mjs.map +1 -1
- package/dist/esm/useFrappeAggregate.native.js +14 -1
- package/dist/esm/useFrappeAggregate.native.js.map +1 -1
- package/dist/esm/useFrappeFacets.mjs +299 -0
- package/dist/esm/useFrappeFacets.mjs.map +1 -0
- package/dist/esm/useFrappeFacets.native.js +599 -0
- package/dist/esm/useFrappeFacets.native.js.map +1 -0
- package/dist/jsx/FrappeAssignment.mjs +2 -0
- package/dist/jsx/FrappeAssignment.mjs.map +1 -1
- package/dist/jsx/FrappeAssignment.native.js +2 -0
- package/dist/jsx/FrappeAssignment.native.js.map +1 -1
- package/dist/jsx/FrappeCalendar.mjs +6 -8
- package/dist/jsx/FrappeCalendar.mjs.map +1 -1
- package/dist/jsx/FrappeCalendar.native.js +5 -9
- package/dist/jsx/FrappeCalendar.native.js.map +1 -1
- package/dist/jsx/FrappeChartCard.mjs +2 -0
- package/dist/jsx/FrappeChartCard.mjs.map +1 -1
- package/dist/jsx/FrappeChartCard.native.js +2 -0
- package/dist/jsx/FrappeChartCard.native.js.map +1 -1
- package/dist/jsx/FrappeDashboard.mjs +12 -19
- package/dist/jsx/FrappeDashboard.mjs.map +1 -1
- package/dist/jsx/FrappeDashboard.native.js +20 -22
- package/dist/jsx/FrappeDashboard.native.js.map +1 -1
- package/dist/jsx/FrappeForm.mjs +3 -1
- package/dist/jsx/FrappeForm.mjs.map +1 -1
- package/dist/jsx/FrappeForm.native.js +3 -1
- package/dist/jsx/FrappeForm.native.js.map +1 -1
- package/dist/jsx/FrappeGantt.mjs +6 -8
- package/dist/jsx/FrappeGantt.mjs.map +1 -1
- package/dist/jsx/FrappeGantt.native.js +5 -9
- package/dist/jsx/FrappeGantt.native.js.map +1 -1
- package/dist/jsx/FrappeImageView.mjs +3 -1
- package/dist/jsx/FrappeImageView.mjs.map +1 -1
- package/dist/jsx/FrappeImageView.native.js +3 -1
- package/dist/jsx/FrappeImageView.native.js.map +1 -1
- package/dist/jsx/FrappeKPICard.mjs +2 -0
- package/dist/jsx/FrappeKPICard.mjs.map +1 -1
- package/dist/jsx/FrappeKPICard.native.js +2 -0
- package/dist/jsx/FrappeKPICard.native.js.map +1 -1
- package/dist/jsx/FrappeKanban.mjs +3 -1
- package/dist/jsx/FrappeKanban.mjs.map +1 -1
- package/dist/jsx/FrappeKanban.native.js +3 -1
- package/dist/jsx/FrappeKanban.native.js.map +1 -1
- package/dist/jsx/FrappeLikedBy.mjs +2 -0
- package/dist/jsx/FrappeLikedBy.mjs.map +1 -1
- package/dist/jsx/FrappeLikedBy.native.js +2 -0
- package/dist/jsx/FrappeLikedBy.native.js.map +1 -1
- package/dist/jsx/FrappeList.mjs +3 -1
- package/dist/jsx/FrappeList.mjs.map +1 -1
- package/dist/jsx/FrappeList.native.js +3 -1
- package/dist/jsx/FrappeList.native.js.map +1 -1
- package/dist/jsx/FrappeListView.mjs +7 -13
- package/dist/jsx/FrappeListView.mjs.map +1 -1
- package/dist/jsx/FrappeListView.native.js +13 -12
- package/dist/jsx/FrappeListView.native.js.map +1 -1
- package/dist/jsx/FrappeProvider.mjs +5 -1
- package/dist/jsx/FrappeProvider.mjs.map +1 -1
- package/dist/jsx/FrappeProvider.native.js +5 -1
- package/dist/jsx/FrappeProvider.native.js.map +1 -1
- package/dist/jsx/FrappeReportBuilder.mjs +2 -0
- package/dist/jsx/FrappeReportBuilder.mjs.map +1 -1
- package/dist/jsx/FrappeReportBuilder.native.js +2 -0
- package/dist/jsx/FrappeReportBuilder.native.js.map +1 -1
- package/dist/jsx/FrappeReportView.mjs +26 -49
- package/dist/jsx/FrappeReportView.mjs.map +1 -1
- package/dist/jsx/FrappeReportView.native.js +35 -50
- package/dist/jsx/FrappeReportView.native.js.map +1 -1
- package/dist/jsx/FrappeTable.mjs +107 -33
- package/dist/jsx/FrappeTable.mjs.map +1 -1
- package/dist/jsx/FrappeTable.native.js +140 -42
- package/dist/jsx/FrappeTable.native.js.map +1 -1
- package/dist/jsx/FrappeTreeView.mjs +3 -1
- package/dist/jsx/FrappeTreeView.mjs.map +1 -1
- package/dist/jsx/FrappeTreeView.native.js +3 -1
- package/dist/jsx/FrappeTreeView.native.js.map +1 -1
- package/dist/jsx/SyncErrorNotice.mjs +51 -0
- package/dist/jsx/SyncErrorNotice.mjs.map +1 -0
- package/dist/jsx/SyncErrorNotice.native.js +92 -0
- package/dist/jsx/SyncErrorNotice.native.js.map +1 -0
- package/dist/jsx/commentActions.mjs +95 -0
- package/dist/jsx/commentActions.mjs.map +1 -0
- package/dist/jsx/commentActions.native.js +129 -0
- package/dist/jsx/commentActions.native.js.map +1 -0
- package/dist/jsx/components/attachments.mjs +2 -0
- package/dist/jsx/components/attachments.mjs.map +1 -1
- package/dist/jsx/components/attachments.native.js +2 -0
- package/dist/jsx/components/attachments.native.js.map +1 -1
- package/dist/jsx/components/tags.mjs +5 -0
- package/dist/jsx/components/tags.mjs.map +1 -1
- package/dist/jsx/components/tags.native.js +5 -0
- package/dist/jsx/components/tags.native.js.map +1 -1
- package/dist/jsx/components/timeline.mjs +31 -4
- package/dist/jsx/components/timeline.mjs.map +1 -1
- package/dist/jsx/components/timeline.native.js +33 -2
- package/dist/jsx/components/timeline.native.js.map +1 -1
- package/dist/jsx/desk/DeskFormSidebar.mjs +2 -0
- package/dist/jsx/desk/DeskFormSidebar.mjs.map +1 -1
- package/dist/jsx/desk/DeskFormSidebar.native.js +2 -0
- package/dist/jsx/desk/DeskFormSidebar.native.js.map +1 -1
- package/dist/jsx/fieldRegistry.mjs +2 -0
- package/dist/jsx/fieldRegistry.mjs.map +1 -1
- package/dist/jsx/fieldRegistry.native.js +2 -0
- package/dist/jsx/fieldRegistry.native.js.map +1 -1
- package/dist/jsx/fields/Date/index.mjs +73 -9
- package/dist/jsx/fields/Date/index.mjs.map +1 -1
- package/dist/jsx/fields/Date/index.native.js +325 -37
- package/dist/jsx/fields/Date/index.native.js.map +1 -1
- package/dist/jsx/fields/Date/types.mjs +2 -0
- package/dist/jsx/fields/Date/types.mjs.map +1 -0
- package/dist/jsx/fields/Date/types.native.js +21 -0
- package/dist/jsx/fields/Date/types.native.js.map +1 -0
- package/dist/jsx/fields/Date/wire.mjs +4 -8
- package/dist/jsx/fields/Date/wire.mjs.map +1 -1
- package/dist/jsx/fields/Date/wire.native.js +4 -11
- package/dist/jsx/fields/Date/wire.native.js.map +1 -1
- package/dist/jsx/fields/DynamicLink.mjs +2 -0
- package/dist/jsx/fields/DynamicLink.mjs.map +1 -1
- package/dist/jsx/fields/DynamicLink.native.js +2 -0
- package/dist/jsx/fields/DynamicLink.native.js.map +1 -1
- package/dist/jsx/fields/FileUpload.mjs +2 -0
- package/dist/jsx/fields/FileUpload.mjs.map +1 -1
- package/dist/jsx/fields/FileUpload.native.js +2 -0
- package/dist/jsx/fields/FileUpload.native.js.map +1 -1
- package/dist/jsx/fields/TableMultiSelect.mjs +7 -2
- package/dist/jsx/fields/TableMultiSelect.mjs.map +1 -1
- package/dist/jsx/fields/TableMultiSelect.native.js +6 -1
- package/dist/jsx/fields/TableMultiSelect.native.js.map +1 -1
- package/dist/jsx/frappeStoryTarget.mjs +88 -2
- package/dist/jsx/frappeStoryTarget.mjs.map +1 -1
- package/dist/jsx/frappeStoryTarget.native.js +100 -0
- package/dist/jsx/frappeStoryTarget.native.js.map +1 -1
- package/dist/jsx/frappeUsers.mjs +2 -0
- package/dist/jsx/frappeUsers.mjs.map +1 -1
- package/dist/jsx/frappeUsers.native.js +2 -0
- package/dist/jsx/frappeUsers.native.js.map +1 -1
- package/dist/jsx/index.js +7 -3
- package/dist/jsx/index.js.map +1 -1
- package/dist/jsx/index.mjs +7 -3
- package/dist/jsx/index.mjs.map +1 -1
- package/dist/jsx/index.native.js +17 -4
- package/dist/jsx/index.native.js.map +1 -1
- package/dist/jsx/liveErrors.mjs +5 -1
- package/dist/jsx/liveErrors.mjs.map +1 -1
- package/dist/jsx/liveErrors.native.js +5 -1
- package/dist/jsx/liveErrors.native.js.map +1 -1
- package/dist/jsx/useFrappeAggregate.mjs +13 -1
- package/dist/jsx/useFrappeAggregate.mjs.map +1 -1
- package/dist/jsx/useFrappeAggregate.native.js +14 -1
- package/dist/jsx/useFrappeAggregate.native.js.map +1 -1
- package/dist/jsx/useFrappeFacets.mjs +299 -0
- package/dist/jsx/useFrappeFacets.mjs.map +1 -0
- package/dist/jsx/useFrappeFacets.native.js +632 -0
- package/dist/jsx/useFrappeFacets.native.js.map +1 -0
- package/package.json +20 -20
- package/src/FormSidebar.stories.tsx +88 -10
- package/src/FrappeAssignment.tsx +2 -0
- package/src/FrappeCalendar.tsx +12 -7
- package/src/FrappeChartCard.tsx +9 -2
- package/src/FrappeDashboard.spec.tsx +95 -0
- package/src/FrappeDashboard.tsx +20 -41
- package/src/FrappeForm.stories.tsx +53 -46
- package/src/FrappeForm.tsx +11 -1
- package/src/FrappeGantt.tsx +12 -7
- package/src/FrappeImageView.tsx +11 -1
- package/src/FrappeKPICard.stories.tsx +4 -1
- package/src/FrappeKPICard.tsx +2 -0
- package/src/FrappeKanban.tsx +4 -0
- package/src/FrappeLikedBy.stories.tsx +26 -10
- package/src/FrappeLikedBy.tsx +2 -0
- package/src/FrappeList.tsx +11 -1
- package/src/FrappeListView.spec.tsx +144 -0
- package/src/FrappeListView.tsx +11 -23
- package/src/FrappeProvider.tsx +30 -2
- package/src/FrappeReportBuilder.tsx +2 -0
- package/src/FrappeReportView.spec.tsx +140 -16
- package/src/FrappeReportView.stories.tsx +12 -6
- package/src/FrappeReportView.tsx +62 -61
- package/src/FrappeTable.keepRows.spec.tsx +200 -0
- package/src/FrappeTable.retryStorm.spec.tsx +140 -0
- package/src/FrappeTable.spec.tsx +222 -0
- package/src/FrappeTable.tsx +211 -54
- package/src/FrappeTreeView.tsx +11 -1
- package/src/SyncErrorNotice.spec.tsx +60 -0
- package/src/SyncErrorNotice.tsx +81 -0
- package/src/commentActions.ts +157 -0
- package/src/components/attachments.tsx +2 -0
- package/src/components/tags.tsx +14 -1
- package/src/components/timeline.optimistic.spec.tsx +106 -0
- package/src/components/timeline.tsx +36 -4
- package/src/desk/DeskFormSidebar.tsx +2 -0
- package/src/fieldRegistry.ts +2 -0
- package/src/fields/Date/index.native.tsx +410 -0
- package/src/fields/Date/index.tsx +138 -57
- package/src/fields/Date/nativePresentation.spec.tsx +251 -0
- package/src/fields/Date/typeParity.spec.tsx +480 -0
- package/src/fields/Date/types.ts +53 -0
- package/src/fields/Date/wire.spec.ts +63 -1
- package/src/fields/Date/wire.ts +17 -17
- package/src/fields/DynamicLink.tsx +2 -0
- package/src/fields/FileUpload.tsx +2 -0
- package/src/fields/TableMultiSelect.tsx +6 -3
- package/src/frappeStoryTarget.tsx +104 -1
- package/src/frappeUsers.ts +2 -0
- package/src/index.spec.ts +62 -0
- package/src/index.ts +36 -2
- package/src/liveErrors.spec.ts +50 -0
- package/src/liveErrors.ts +11 -1
- package/src/useFrappeAggregate.ts +20 -1
- package/src/useFrappeFacets.spec.tsx +436 -0
- package/src/useFrappeFacets.ts +568 -0
- package/types/FrappeAssignment.d.ts.map +1 -1
- package/types/FrappeCalendar.d.ts.map +1 -1
- package/types/FrappeChartCard.d.ts +1 -2
- package/types/FrappeChartCard.d.ts.map +1 -1
- package/types/FrappeDashboard.d.ts +0 -1
- package/types/FrappeDashboard.d.ts.map +1 -1
- package/types/FrappeForm.d.ts.map +1 -1
- package/types/FrappeGantt.d.ts.map +1 -1
- package/types/FrappeKPICard.d.ts.map +1 -1
- package/types/FrappeKanban.d.ts.map +1 -1
- package/types/FrappeLikedBy.d.ts.map +1 -1
- package/types/FrappeList.d.ts.map +1 -1
- package/types/FrappeListView.d.ts.map +1 -1
- package/types/FrappeProvider.d.ts +18 -1
- package/types/FrappeProvider.d.ts.map +1 -1
- package/types/FrappeReportBuilder.d.ts.map +1 -1
- package/types/FrappeReportView.d.ts +22 -15
- package/types/FrappeReportView.d.ts.map +1 -1
- package/types/FrappeTable.d.ts +21 -2
- package/types/FrappeTable.d.ts.map +1 -1
- package/types/FrappeTreeView.d.ts.map +1 -1
- package/types/SyncErrorNotice.d.ts +38 -0
- package/types/SyncErrorNotice.d.ts.map +1 -0
- package/types/commentActions.d.ts +46 -0
- package/types/commentActions.d.ts.map +1 -0
- package/types/components/attachments.d.ts.map +1 -1
- package/types/components/tags.d.ts.map +1 -1
- package/types/components/timeline.d.ts.map +1 -1
- package/types/desk/DeskFormSidebar.d.ts.map +1 -1
- package/types/fieldRegistry.d.ts.map +1 -1
- package/types/fields/Date/index.d.ts +22 -12
- package/types/fields/Date/index.d.ts.map +1 -1
- package/types/fields/Date/index.native.d.ts +14 -0
- package/types/fields/Date/index.native.d.ts.map +1 -0
- package/types/fields/Date/types.d.ts +27 -0
- package/types/fields/Date/types.d.ts.map +1 -0
- package/types/fields/Date/wire.d.ts +13 -9
- package/types/fields/Date/wire.d.ts.map +1 -1
- package/types/fields/DynamicLink.d.ts.map +1 -1
- package/types/fields/FileUpload.d.ts.map +1 -1
- package/types/fields/TableMultiSelect.d.ts.map +1 -1
- package/types/frappeStoryTarget.d.ts +19 -1
- package/types/frappeStoryTarget.d.ts.map +1 -1
- package/types/frappeUsers.d.ts.map +1 -1
- package/types/index.d.ts +6 -2
- package/types/index.d.ts.map +1 -1
- package/types/liveErrors.d.ts +4 -0
- package/types/liveErrors.d.ts.map +1 -1
- package/types/useFrappeAggregate.d.ts +4 -0
- package/types/useFrappeAggregate.d.ts.map +1 -1
- package/types/useFrappeFacets.d.ts +143 -0
- package/types/useFrappeFacets.d.ts.map +1 -0
- package/dist/cjs/fields/Date/date.native.js +0 -277
- package/dist/cjs/fields/Date/date.native.js.map +0 -1
- package/dist/esm/fields/Date/date.native.js +0 -238
- package/dist/esm/fields/Date/date.native.js.map +0 -1
- package/dist/jsx/fields/Date/date.native.js +0 -277
- package/dist/jsx/fields/Date/date.native.js.map +0 -1
- package/src/fields/Date/date.native.tsx +0 -299
- package/types/fields/Date/date.native.d.ts +0 -19
- package/types/fields/Date/date.native.d.ts.map +0 -1
package/src/FrappeTable.tsx
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
FrappeApp,
|
|
3
3
|
Order,
|
|
4
|
+
matchesFilters,
|
|
4
5
|
useDocTypeMeta,
|
|
5
6
|
useFrappeCollection,
|
|
6
7
|
useFrappeDoctypeEvents,
|
|
@@ -19,7 +20,7 @@ import { AsyncBoundary } from "@multiplatform.one/components";
|
|
|
19
20
|
import { useResolvedKnobs } from "@multiplatform.one/theme";
|
|
20
21
|
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
21
22
|
import { useTranslation } from "react-i18next";
|
|
22
|
-
import {
|
|
23
|
+
import { XStack, YStack } from "tamagui";
|
|
23
24
|
import { FilterManager, SavedFilterDropdown, useSavedFilters } from "@multiplatform.one/table";
|
|
24
25
|
import { useFrappeConfig } from "./FrappeProvider";
|
|
25
26
|
import { FrappeReconnectIndicator } from "./FrappeReconnectIndicator";
|
|
@@ -56,7 +57,14 @@ export interface FrappeTableProps<TDoc extends { name: string; doctype: string }
|
|
|
56
57
|
fields?: readonly (keyof TDoc & string)[];
|
|
57
58
|
/** Initial filter criteria */
|
|
58
59
|
filters?: Partial<TDoc> | Array<[keyof TDoc & string, string, any]>;
|
|
59
|
-
/**
|
|
60
|
+
/**
|
|
61
|
+
* Callback when a row is activated. Rides DataTable's first-class
|
|
62
|
+
* `onRowPress` contract: the whole row is ONE pressable unit (no
|
|
63
|
+
* per-cell pressable wrappers), the row set exposes a single roving tab
|
|
64
|
+
* stop (LC-49), Enter AND Space activate the focused row (LC-50), and
|
|
65
|
+
* presses on inner interactive controls (selection checkboxes, buttons,
|
|
66
|
+
* links) never fire the row press.
|
|
67
|
+
*/
|
|
60
68
|
onRowClick?: (row: Partial<TDoc>) => void;
|
|
61
69
|
/** Custom loading indicator */
|
|
62
70
|
loading?: React.ReactNode;
|
|
@@ -79,6 +87,18 @@ export interface FrappeTableProps<TDoc extends { name: string; doctype: string }
|
|
|
79
87
|
* Used for server-side fetches when serverSide is true.
|
|
80
88
|
*/
|
|
81
89
|
onStateChange?: (state: DataTableState) => void;
|
|
90
|
+
/**
|
|
91
|
+
* In-place refresh channel: bump the value to re-run the current query
|
|
92
|
+
* (serverSide mode) without remounting — rows stay rendered and swap when
|
|
93
|
+
* the response lands (keep-rows-and-reconcile; never key the table on a
|
|
94
|
+
* refresh generation).
|
|
95
|
+
*/
|
|
96
|
+
refreshToken?: unknown;
|
|
97
|
+
/**
|
|
98
|
+
* Density. DataTableRoot accepts this; it is not on DataTableProps, so
|
|
99
|
+
* FrappeTable has to name it or callers (backoffice list) fail the build.
|
|
100
|
+
*/
|
|
101
|
+
compact?: boolean;
|
|
82
102
|
}
|
|
83
103
|
|
|
84
104
|
/**
|
|
@@ -182,6 +202,26 @@ function isOperatorFilterValue(value: unknown): value is FilterValue {
|
|
|
182
202
|
);
|
|
183
203
|
}
|
|
184
204
|
|
|
205
|
+
/** Sort the current page locally so a sort change is visible before reconcile. */
|
|
206
|
+
function sortRowsLocally<T extends Record<string, unknown>>(
|
|
207
|
+
rows: T[],
|
|
208
|
+
orderBy: Array<{ field: string; order: Order }> | undefined,
|
|
209
|
+
): T[] {
|
|
210
|
+
if (!orderBy?.length) return rows;
|
|
211
|
+
return [...rows].sort((a, b) => {
|
|
212
|
+
for (const { field, order } of orderBy) {
|
|
213
|
+
const av = a[field];
|
|
214
|
+
const bv = b[field];
|
|
215
|
+
if (av == null && bv == null) continue;
|
|
216
|
+
if (av == null) return order === Order.Asc ? -1 : 1;
|
|
217
|
+
if (bv == null) return order === Order.Asc ? 1 : -1;
|
|
218
|
+
if (av < bv) return order === Order.Asc ? -1 : 1;
|
|
219
|
+
if (av > bv) return order === Order.Asc ? 1 : -1;
|
|
220
|
+
}
|
|
221
|
+
return 0;
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
|
|
185
225
|
/** Convert DataTableState to Frappe API params */
|
|
186
226
|
function stateToFrappeParams(state: DataTableState) {
|
|
187
227
|
const { pagination, sorting, filters, columnFilters, globalFilter } = state;
|
|
@@ -233,9 +273,11 @@ export function FrappeTable<TDoc extends { name: string; doctype: string }>({
|
|
|
233
273
|
userId,
|
|
234
274
|
serverSide = false,
|
|
235
275
|
onStateChange: onStateChangeProp,
|
|
276
|
+
refreshToken,
|
|
236
277
|
paginationMode = "pages",
|
|
237
278
|
loadPage: loadPageProp,
|
|
238
279
|
filterableFields: filterableFieldsProp,
|
|
280
|
+
layout,
|
|
239
281
|
...dataTableProps
|
|
240
282
|
}: FrappeTableProps<TDoc>) {
|
|
241
283
|
// Infinite + serverSide: DataTable's page cache drives fetching through a
|
|
@@ -250,6 +292,20 @@ export function FrappeTable<TDoc extends { name: string; doctype: string }>({
|
|
|
250
292
|
>(initialFilters);
|
|
251
293
|
const config = useFrappeConfig();
|
|
252
294
|
|
|
295
|
+
// Adopt `filters` prop CONTENT changes in place (client/live-collection
|
|
296
|
+
// mode) — the keep-rows counterpart of DataTable's controlledState: outer
|
|
297
|
+
// filter surfaces update the query without remounting the table. Content-
|
|
298
|
+
// keyed so unstable array identities from hosts are safe; saved-filter
|
|
299
|
+
// selections (setActiveFilters below) are not overridden by echoes.
|
|
300
|
+
const filtersPropKey = initialFilters !== undefined ? JSON.stringify(initialFilters) : undefined;
|
|
301
|
+
const adoptedFiltersPropRef = useRef(filtersPropKey);
|
|
302
|
+
useEffect(() => {
|
|
303
|
+
if (filtersPropKey === undefined) return;
|
|
304
|
+
if (adoptedFiltersPropRef.current === filtersPropKey) return;
|
|
305
|
+
adoptedFiltersPropRef.current = filtersPropKey;
|
|
306
|
+
setActiveFilters(JSON.parse(filtersPropKey));
|
|
307
|
+
}, [filtersPropKey]);
|
|
308
|
+
|
|
253
309
|
const finalBaseURL = baseURL ?? config?.baseURL;
|
|
254
310
|
const finalAuth = auth ?? config?.auth;
|
|
255
311
|
const finalSyncOptions = syncOptions ?? config?.syncOptions;
|
|
@@ -264,11 +320,21 @@ export function FrappeTable<TDoc extends { name: string; doctype: string }>({
|
|
|
264
320
|
() => ({
|
|
265
321
|
baseURL: finalBaseURL,
|
|
266
322
|
socketPort: config?.socketPort,
|
|
323
|
+
siteName: config?.siteName,
|
|
324
|
+
socketBaseURL: config?.socketBaseURL,
|
|
267
325
|
auth: finalAuth,
|
|
268
326
|
syncOptions: finalSyncOptions,
|
|
269
327
|
fixtures: config?.fixtures,
|
|
270
328
|
}),
|
|
271
|
-
[
|
|
329
|
+
[
|
|
330
|
+
finalBaseURL,
|
|
331
|
+
config?.socketPort,
|
|
332
|
+
config?.siteName,
|
|
333
|
+
config?.socketBaseURL,
|
|
334
|
+
finalAuth,
|
|
335
|
+
finalSyncOptions,
|
|
336
|
+
config?.fixtures,
|
|
337
|
+
],
|
|
272
338
|
);
|
|
273
339
|
|
|
274
340
|
const frappeApp = useMemo(() => {
|
|
@@ -287,12 +353,28 @@ export function FrappeTable<TDoc extends { name: string; doctype: string }>({
|
|
|
287
353
|
);
|
|
288
354
|
const filterableFields = filterableFieldsProp ?? metaFilterFields;
|
|
289
355
|
|
|
356
|
+
// Accessible row identity (DG-A11Y-06) for DataTable's row-press label
|
|
357
|
+
// and selection-checkbox names: the doctype's title-field value when
|
|
358
|
+
// meta is loaded, else the document name. An empty result defers to
|
|
359
|
+
// DataTable's own fallbacks (first visible cell → positional "Row N").
|
|
360
|
+
const titleField = meta?.title_field;
|
|
361
|
+
const rowLabel = useCallback(
|
|
362
|
+
(row: Partial<TDoc>): string => {
|
|
363
|
+
const fromTitle = titleField ? (row as Record<string, unknown>)[titleField] : undefined;
|
|
364
|
+
if (typeof fromTitle === "string" && fromTitle) return fromTitle;
|
|
365
|
+
if (typeof fromTitle === "number") return String(fromTitle);
|
|
366
|
+
return typeof row.name === "string" ? row.name : "";
|
|
367
|
+
},
|
|
368
|
+
[titleField],
|
|
369
|
+
);
|
|
370
|
+
|
|
290
371
|
const [serverData, setServerData] = useState<TDoc[]>([]);
|
|
291
372
|
const [serverRowCount, setServerRowCount] = useState(0);
|
|
292
373
|
const [serverLoading, setServerLoading] = useState(serverSide);
|
|
293
374
|
// First fetch completed (success or failure) — later live refetches must
|
|
294
375
|
// swap rows in place without flashing the skeleton over the table.
|
|
295
376
|
const [hasLoadedOnce, setHasLoadedOnce] = useState(false);
|
|
377
|
+
const hasLoadedOnceRef = useRef(false);
|
|
296
378
|
const lastFetchKey = useRef<string>("");
|
|
297
379
|
const fetchSeq = useRef(0);
|
|
298
380
|
/** In-flight serverSide request — superseding fetches and unmount abort it. */
|
|
@@ -300,6 +382,15 @@ export function FrappeTable<TDoc extends { name: string; doctype: string }>({
|
|
|
300
382
|
/** Last table state fetched — realtime refetches re-run the same query. */
|
|
301
383
|
const lastStateRef = useRef<DataTableState | null>(null);
|
|
302
384
|
|
|
385
|
+
/** Filters / sort of the last issued query — detects changes for the
|
|
386
|
+
* optimistic local apply below. */
|
|
387
|
+
const lastFiltersKey = useRef<string>("");
|
|
388
|
+
const lastSortKey = useRef<string>("");
|
|
389
|
+
/** Last successful rows — held as placeholderData while a refetch is in
|
|
390
|
+
* flight so a local apply that would empty the current page cannot blank
|
|
391
|
+
* the body. */
|
|
392
|
+
const lastGoodServerRows = useRef<TDoc[]>([]);
|
|
393
|
+
|
|
303
394
|
const fetchServerData = useCallback(
|
|
304
395
|
async (state: DataTableState) => {
|
|
305
396
|
if (!frappeApp || !serverSide) return;
|
|
@@ -308,6 +399,41 @@ export function FrappeTable<TDoc extends { name: string; doctype: string }>({
|
|
|
308
399
|
const key = JSON.stringify(params);
|
|
309
400
|
if (key === lastFetchKey.current) return;
|
|
310
401
|
lastFetchKey.current = key;
|
|
402
|
+
// Optimistic local apply (keep-rows-and-reconcile / keepPreviousData):
|
|
403
|
+
// filter narrows the CURRENT page when it leaves rows; sort reorders
|
|
404
|
+
// what is on screen; pagination keeps the current page as placeholder
|
|
405
|
+
// until the new page arrives. A local pass that would empty the body
|
|
406
|
+
// is skipped — previous rows stay mounted until reconcile.
|
|
407
|
+
const filtersKey = JSON.stringify(params.filters);
|
|
408
|
+
const sortKey = JSON.stringify(params.orderBy ?? []);
|
|
409
|
+
const alreadyLoaded = lastFiltersKey.current !== "" || lastGoodServerRows.current.length > 0;
|
|
410
|
+
if (alreadyLoaded) {
|
|
411
|
+
const filtersChanged = filtersKey !== lastFiltersKey.current;
|
|
412
|
+
const sortChanged = sortKey !== lastSortKey.current;
|
|
413
|
+
if (filtersChanged || sortChanged) {
|
|
414
|
+
setServerData((previous) => {
|
|
415
|
+
let next = previous;
|
|
416
|
+
if (filtersChanged && params.filters.length > 0) {
|
|
417
|
+
const narrowed = previous.filter((row) =>
|
|
418
|
+
matchesFilters(
|
|
419
|
+
row as unknown as Parameters<typeof matchesFilters>[0],
|
|
420
|
+
params.filters,
|
|
421
|
+
),
|
|
422
|
+
);
|
|
423
|
+
if (narrowed.length > 0) next = narrowed;
|
|
424
|
+
}
|
|
425
|
+
if (sortChanged && params.orderBy?.length) {
|
|
426
|
+
next = sortRowsLocally(
|
|
427
|
+
next as unknown as Record<string, unknown>[],
|
|
428
|
+
params.orderBy,
|
|
429
|
+
) as TDoc[];
|
|
430
|
+
}
|
|
431
|
+
return next;
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
lastFiltersKey.current = filtersKey;
|
|
436
|
+
lastSortKey.current = sortKey;
|
|
311
437
|
// Abort the superseded request (SB-M-011): the sequence guard below
|
|
312
438
|
// already drops its response, aborting stops the wasted transfer too.
|
|
313
439
|
fetchAbortRef.current?.abort();
|
|
@@ -339,17 +465,23 @@ export function FrappeTable<TDoc extends { name: string; doctype: string }>({
|
|
|
339
465
|
if (seq !== fetchSeq.current) return;
|
|
340
466
|
setServerData(data);
|
|
341
467
|
setServerRowCount(count);
|
|
468
|
+
lastGoodServerRows.current = data;
|
|
342
469
|
setErrorState(null);
|
|
343
470
|
} catch (err) {
|
|
344
471
|
if (seq !== fetchSeq.current) return;
|
|
345
472
|
// Abort is a cancellation (unmount), not a failure to surface.
|
|
346
473
|
if (controller.signal.aborted) return;
|
|
347
474
|
setErrorState(err instanceof Error ? err : new Error(String(err)));
|
|
348
|
-
|
|
349
|
-
|
|
475
|
+
// Keep last-good rows on a refetch failure — only the first load
|
|
476
|
+
// may settle as empty.
|
|
477
|
+
if (!hasLoadedOnceRef.current) {
|
|
478
|
+
setServerData([]);
|
|
479
|
+
setServerRowCount(0);
|
|
480
|
+
}
|
|
350
481
|
} finally {
|
|
351
482
|
if (seq === fetchSeq.current && !controller.signal.aborted) {
|
|
352
483
|
setServerLoading(false);
|
|
484
|
+
hasLoadedOnceRef.current = true;
|
|
353
485
|
setHasLoadedOnce(true);
|
|
354
486
|
}
|
|
355
487
|
}
|
|
@@ -384,6 +516,19 @@ export function FrappeTable<TDoc extends { name: string; doctype: string }>({
|
|
|
384
516
|
[],
|
|
385
517
|
);
|
|
386
518
|
|
|
519
|
+
// In-place refresh: re-run the current query when the host bumps
|
|
520
|
+
// refreshToken. Rows stay rendered (hasLoadedOnce is untouched) and swap
|
|
521
|
+
// when the response lands — the no-remount replacement for keying the
|
|
522
|
+
// table on a refresh generation.
|
|
523
|
+
const refreshTokenSeen = useRef(refreshToken);
|
|
524
|
+
useEffect(() => {
|
|
525
|
+
if (refreshTokenSeen.current === refreshToken) return;
|
|
526
|
+
refreshTokenSeen.current = refreshToken;
|
|
527
|
+
if (!serverSide || infiniteServerMode) return;
|
|
528
|
+
lastFetchKey.current = "";
|
|
529
|
+
if (lastStateRef.current) fetchServerData(lastStateRef.current);
|
|
530
|
+
}, [refreshToken, serverSide, infiniteServerMode, fetchServerData]);
|
|
531
|
+
|
|
387
532
|
// Page loader for infinite mode: same param mapping as fetchServerData but
|
|
388
533
|
// per-page and cacheable — DataTable pre-loads page N+1 while N is on
|
|
389
534
|
// screen. totalCount rides page 0 only (one count query per query state).
|
|
@@ -427,17 +572,20 @@ export function FrappeTable<TDoc extends { name: string; doctype: string }>({
|
|
|
427
572
|
useEffect(() => {
|
|
428
573
|
if (serverSide && !infiniteServerMode && frappeApp) {
|
|
429
574
|
lastFetchKey.current = "";
|
|
430
|
-
fetchServerData(
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
575
|
+
fetchServerData(
|
|
576
|
+
lastStateRef.current ??
|
|
577
|
+
({
|
|
578
|
+
data: [],
|
|
579
|
+
columns: columns as any,
|
|
580
|
+
pagination: { pageIndex: 0, pageSize: 10 },
|
|
581
|
+
sorting: [],
|
|
582
|
+
filters: [],
|
|
583
|
+
columnFilters: [],
|
|
584
|
+
columnVisibility: {},
|
|
585
|
+
columnOrder: columns.map((c) => c.id ?? ""),
|
|
586
|
+
columnSizing: {},
|
|
587
|
+
} as DataTableState),
|
|
588
|
+
);
|
|
441
589
|
}
|
|
442
590
|
}, [serverSide, infiniteServerMode, !!frappeApp, fetchServerData]);
|
|
443
591
|
|
|
@@ -471,16 +619,45 @@ export function FrappeTable<TDoc extends { name: string; doctype: string }>({
|
|
|
471
619
|
} = useLiveQuery((q) => (collection ? q.from({ _list: collection }) : null), [collection]);
|
|
472
620
|
const clientRows = useMemo(() => (rawData as TDoc[] | undefined) ?? [], [rawData]);
|
|
473
621
|
|
|
474
|
-
|
|
622
|
+
// Keep-rows-and-reconcile for client/live-collection mode: a filter change
|
|
623
|
+
// swaps the collection (new subscription), which reports loading from
|
|
624
|
+
// empty — render the LAST GOOD rows, locally narrowed by the new filters
|
|
625
|
+
// (optimistic local apply), until the new collection is ready. Only the
|
|
626
|
+
// very first load may show the skeleton.
|
|
627
|
+
const lastGoodClientRows = useRef<TDoc[] | null>(null);
|
|
628
|
+
if (!serverSide && !isRowsLoading) lastGoodClientRows.current = clientRows;
|
|
629
|
+
const clientRowsResolved = useMemo(() => {
|
|
630
|
+
if (serverSide) return clientRows;
|
|
631
|
+
if (!isRowsLoading || !lastGoodClientRows.current) return clientRows;
|
|
632
|
+
const held = lastGoodClientRows.current;
|
|
633
|
+
if (!activeFilters) return held;
|
|
634
|
+
const narrowed = held.filter((row) =>
|
|
635
|
+
matchesFilters(row as unknown as Parameters<typeof matchesFilters>[0], activeFilters),
|
|
636
|
+
);
|
|
637
|
+
// A local pass that would empty the body is skipped — held rows stay
|
|
638
|
+
// as placeholderData until the new collection reconciles.
|
|
639
|
+
return narrowed.length > 0 ? narrowed : held;
|
|
640
|
+
}, [serverSide, clientRows, isRowsLoading, activeFilters]);
|
|
641
|
+
|
|
642
|
+
const serverRowsResolved =
|
|
643
|
+
serverSide && serverLoading && serverData.length === 0 && lastGoodServerRows.current.length > 0
|
|
644
|
+
? lastGoodServerRows.current
|
|
645
|
+
: serverData;
|
|
646
|
+
const rows = serverSide ? serverRowsResolved : clientRowsResolved;
|
|
475
647
|
// Infinite serverSide: DataTable owns loading/empty chrome (skeleton +
|
|
476
648
|
// honest end state) — FrappeTable must not early-return on its own.
|
|
477
649
|
// Paged serverSide: only the FIRST load shows the skeleton; live
|
|
478
650
|
// refetches swap rows in place without flashing (LC-06-adjacent).
|
|
651
|
+
// Client mode: the skeleton shows only before the FIRST ready pass —
|
|
652
|
+
// collection swaps keep the last-good rows rendered.
|
|
479
653
|
const isRowsLoadingResolved = serverSide
|
|
480
654
|
? infiniteServerMode
|
|
481
655
|
? false
|
|
482
656
|
: serverLoading && !hasLoadedOnce
|
|
483
|
-
: isRowsLoading;
|
|
657
|
+
: isRowsLoading && lastGoodClientRows.current === null;
|
|
658
|
+
const isFetching = serverSide
|
|
659
|
+
? !infiniteServerMode && serverLoading && hasLoadedOnce
|
|
660
|
+
: isRowsLoading && lastGoodClientRows.current !== null;
|
|
484
661
|
|
|
485
662
|
// Reset stale errors when the underlying collection changes (new doctype
|
|
486
663
|
// or filters) so a recovered query doesn't keep showing the old failure.
|
|
@@ -525,11 +702,13 @@ export function FrappeTable<TDoc extends { name: string; doctype: string }>({
|
|
|
525
702
|
const rowData = rows ?? [];
|
|
526
703
|
// Honest states via AsyncBoundary (DG-ST-01: error > loading > empty).
|
|
527
704
|
// Infinite serverSide delegates loading/empty chrome to DataTable.
|
|
705
|
+
// After the first load, NEVER replace DataTable with empty/loading chrome
|
|
706
|
+
// while a refetch is in flight — that was the named blank-the-list defect.
|
|
528
707
|
const boundaryState = errorState
|
|
529
708
|
? { error: errorState }
|
|
530
709
|
: isRowsLoadingResolved
|
|
531
710
|
? { loading: loading ?? true }
|
|
532
|
-
: rowData.length === 0 && !infiniteServerMode
|
|
711
|
+
: rowData.length === 0 && !infiniteServerMode && !isFetching
|
|
533
712
|
? { empty: true }
|
|
534
713
|
: null;
|
|
535
714
|
if (boundaryState) {
|
|
@@ -537,7 +716,7 @@ export function FrappeTable<TDoc extends { name: string; doctype: string }>({
|
|
|
537
716
|
<YStack {...knobProps.gap} flex={1}>
|
|
538
717
|
<FrappeReconnectIndicator config={connectionConfig} />
|
|
539
718
|
<AsyncBoundary
|
|
540
|
-
layout="table"
|
|
719
|
+
layout={layout === "list" || layout === "cards" ? "list" : "table"}
|
|
541
720
|
errorTitle={t("Error loading {{doctype}}", { doctype })}
|
|
542
721
|
emptyTitle={t("No {{doctype}} records found", { doctype })}
|
|
543
722
|
{...boundaryState}
|
|
@@ -546,39 +725,6 @@ export function FrappeTable<TDoc extends { name: string; doctype: string }>({
|
|
|
546
725
|
);
|
|
547
726
|
}
|
|
548
727
|
|
|
549
|
-
const columnsWithClick: DataTableColumn<Partial<TDoc>>[] = onRowClick
|
|
550
|
-
? columns.map((col) => ({
|
|
551
|
-
...col,
|
|
552
|
-
cell: (props) => {
|
|
553
|
-
const content = typeof col.cell === "function" ? col.cell(props) : props.getValue();
|
|
554
|
-
return (
|
|
555
|
-
<View
|
|
556
|
-
onPress={() => onRowClick(props.row.original)}
|
|
557
|
-
cursor="pointer"
|
|
558
|
-
hoverStyle={{ backgroundColor: "$color2" }}
|
|
559
|
-
// Cell containment (LC-06/LC-10): a bare View defaults to
|
|
560
|
-
// flexShrink 0 and hugs its content, so long cell content
|
|
561
|
-
// (e.g. a ToDo description) painted PAST the column bounds and
|
|
562
|
-
// under the neighbor cells (the td itself doesn't clip).
|
|
563
|
-
// Shrink with the cell, cap at its box, clip the excess — the
|
|
564
|
-
// inner one-line Texts then ellipsize in-column.
|
|
565
|
-
minWidth={0}
|
|
566
|
-
maxWidth="100%"
|
|
567
|
-
flexShrink={1}
|
|
568
|
-
overflow="hidden"
|
|
569
|
-
>
|
|
570
|
-
{/* raw strings crash native (text must be inside <Text>) */}
|
|
571
|
-
{typeof content === "string" || typeof content === "number" ? (
|
|
572
|
-
<Text fontSize="$3">{content}</Text>
|
|
573
|
-
) : (
|
|
574
|
-
(content as React.ReactNode)
|
|
575
|
-
)}
|
|
576
|
-
</View>
|
|
577
|
-
);
|
|
578
|
-
},
|
|
579
|
-
}))
|
|
580
|
-
: columns;
|
|
581
|
-
|
|
582
728
|
return (
|
|
583
729
|
<YStack {...knobProps.gap} flex={1}>
|
|
584
730
|
<FrappeReconnectIndicator config={connectionConfig} />
|
|
@@ -607,7 +753,16 @@ export function FrappeTable<TDoc extends { name: string; doctype: string }>({
|
|
|
607
753
|
)}
|
|
608
754
|
<DataTable
|
|
609
755
|
data={rowData}
|
|
610
|
-
columns={
|
|
756
|
+
columns={columns}
|
|
757
|
+
// onRowClick (the stable FrappeTable prop name) rides DataTable's
|
|
758
|
+
// first-class row activation: whole-row press target, roving tab
|
|
759
|
+
// stop (LC-49), Enter/Space activation (LC-50), inner interactive
|
|
760
|
+
// controls win. rowLabel names each pressable row and its
|
|
761
|
+
// selection checkbox by document identity. Both spread BEFORE
|
|
762
|
+
// {...dataTableProps} so an explicit consumer onRowPress/rowLabel
|
|
763
|
+
// still ejects (consumer-last house rule).
|
|
764
|
+
{...(onRowClick ? { onRowPress: (row: Partial<TDoc>) => onRowClick(row) } : {})}
|
|
765
|
+
rowLabel={rowLabel}
|
|
611
766
|
{...(serverSide && {
|
|
612
767
|
manualFiltering: true,
|
|
613
768
|
manualPagination: true,
|
|
@@ -618,7 +773,9 @@ export function FrappeTable<TDoc extends { name: string; doctype: string }>({
|
|
|
618
773
|
paginationMode={paginationMode}
|
|
619
774
|
{...(resolvedLoadPage ? { loadPage: resolvedLoadPage } : {})}
|
|
620
775
|
{...(filterableFields ? { filterableFields } : {})}
|
|
776
|
+
isFetching={isFetching}
|
|
621
777
|
{...dataTableProps}
|
|
778
|
+
{...(layout ? { layout } : {})}
|
|
622
779
|
/>
|
|
623
780
|
</YStack>
|
|
624
781
|
);
|
package/src/FrappeTreeView.tsx
CHANGED
|
@@ -130,11 +130,21 @@ export function FrappeTreeView<TDoc extends { name: string; doctype: string }>({
|
|
|
130
130
|
() => ({
|
|
131
131
|
baseURL: finalBaseURL,
|
|
132
132
|
socketPort: config?.socketPort,
|
|
133
|
+
siteName: config?.siteName,
|
|
134
|
+
socketBaseURL: config?.socketBaseURL,
|
|
133
135
|
auth: finalAuth,
|
|
134
136
|
syncOptions: finalSyncOptions,
|
|
135
137
|
fixtures: config?.fixtures,
|
|
136
138
|
}),
|
|
137
|
-
[
|
|
139
|
+
[
|
|
140
|
+
finalBaseURL,
|
|
141
|
+
config?.socketPort,
|
|
142
|
+
config?.siteName,
|
|
143
|
+
config?.socketBaseURL,
|
|
144
|
+
finalAuth,
|
|
145
|
+
finalSyncOptions,
|
|
146
|
+
config?.fixtures,
|
|
147
|
+
],
|
|
138
148
|
);
|
|
139
149
|
|
|
140
150
|
const { meta, isLoading: isMetaLoading, error: metaError } = useDocTypeMeta(appConfig, doctype);
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// SyncErrorNotice contract: the visible half of the rejected-write pattern —
|
|
2
|
+
// role="alert" message, Retry re-issues, Dismiss clears, and each affordance
|
|
3
|
+
// only renders when its handler is provided.
|
|
4
|
+
import { renderWithProviders } from "@multiplatform.one/test-utils";
|
|
5
|
+
import { cleanup, fireEvent, screen } from "@testing-library/react";
|
|
6
|
+
import i18next from "i18next";
|
|
7
|
+
import { initReactI18next } from "react-i18next";
|
|
8
|
+
import { afterEach, beforeAll, describe, expect, it, vi } from "vitest";
|
|
9
|
+
import { SyncErrorNotice } from "./SyncErrorNotice";
|
|
10
|
+
|
|
11
|
+
beforeAll(async () => {
|
|
12
|
+
await i18next.use(initReactI18next).init({
|
|
13
|
+
lng: "en",
|
|
14
|
+
interpolation: { escapeValue: false },
|
|
15
|
+
resources: { en: { translation: {} } },
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
afterEach(cleanup);
|
|
20
|
+
|
|
21
|
+
describe("SyncErrorNotice", () => {
|
|
22
|
+
it("renders the message as an alert", async () => {
|
|
23
|
+
renderWithProviders(<SyncErrorNotice message="Could not add comment" />);
|
|
24
|
+
const alert = await screen.findByRole("alert");
|
|
25
|
+
expect(alert).toHaveTextContent("Could not add comment");
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("fires onRetry and onDismiss from their buttons", async () => {
|
|
29
|
+
const onRetry = vi.fn();
|
|
30
|
+
const onDismiss = vi.fn();
|
|
31
|
+
renderWithProviders(
|
|
32
|
+
<SyncErrorNotice message="Write rejected" onRetry={onRetry} onDismiss={onDismiss} />,
|
|
33
|
+
);
|
|
34
|
+
fireEvent.click(await screen.findByText("Retry"));
|
|
35
|
+
expect(onRetry).toHaveBeenCalledTimes(1);
|
|
36
|
+
fireEvent.click(await screen.findByText("Dismiss"));
|
|
37
|
+
expect(onDismiss).toHaveBeenCalledTimes(1);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it("hides affordances whose handlers are omitted", async () => {
|
|
41
|
+
renderWithProviders(<SyncErrorNotice message="No actions" />);
|
|
42
|
+
await screen.findByRole("alert");
|
|
43
|
+
expect(screen.queryByText("Retry")).toBeNull();
|
|
44
|
+
expect(screen.queryByText("Dismiss")).toBeNull();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("honors custom labels", async () => {
|
|
48
|
+
renderWithProviders(
|
|
49
|
+
<SyncErrorNotice
|
|
50
|
+
message="Custom"
|
|
51
|
+
onRetry={() => {}}
|
|
52
|
+
onDismiss={() => {}}
|
|
53
|
+
retryLabel="Try again"
|
|
54
|
+
dismissLabel="Ignore"
|
|
55
|
+
/>,
|
|
56
|
+
);
|
|
57
|
+
expect(await screen.findByText("Try again")).toBeTruthy();
|
|
58
|
+
expect(await screen.findByText("Ignore")).toBeTruthy();
|
|
59
|
+
});
|
|
60
|
+
});
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { Button } from "@multiplatform.one/components";
|
|
2
|
+
import { useResolvedKnobs } from "@multiplatform.one/theme";
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { useTranslation } from "react-i18next";
|
|
5
|
+
import { Text, XStack } from "tamagui";
|
|
6
|
+
|
|
7
|
+
export interface SyncErrorNoticeProps {
|
|
8
|
+
/** Human-readable failure message (already localized by the caller). */
|
|
9
|
+
message: string;
|
|
10
|
+
/**
|
|
11
|
+
* Re-issues the failed write (same mutation id — idempotent server-side).
|
|
12
|
+
* Omit to hide the Retry affordance.
|
|
13
|
+
*/
|
|
14
|
+
onRetry?: () => void;
|
|
15
|
+
/** Drops the failed entry without retrying. Omit to hide Dismiss. */
|
|
16
|
+
onDismiss?: () => void;
|
|
17
|
+
/** Override the Retry button label (defaults to a localized "Retry"). */
|
|
18
|
+
retryLabel?: string;
|
|
19
|
+
/** Override the Dismiss button label (defaults to a localized "Dismiss"). */
|
|
20
|
+
dismissLabel?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* The rejected-write notice — the visible half of the optimistic mutation
|
|
25
|
+
* contract: when the server rejects a write, the optimistic state has
|
|
26
|
+
* already been rolled back by the sync engine; this primitive surfaces the
|
|
27
|
+
* error inline at the surface that initiated the write, with Retry
|
|
28
|
+
* (re-issues the SAME mutation id) and Dismiss (clears the ledger entry).
|
|
29
|
+
*
|
|
30
|
+
* Pair with `useFrappePendingMutations` from `@multiplatform.one/frappe`:
|
|
31
|
+
*
|
|
32
|
+
* ```tsx
|
|
33
|
+
* const { failed, retry, dismiss } = useFrappePendingMutations(config, "Comment");
|
|
34
|
+
* {failed.map((entry) => (
|
|
35
|
+
* <SyncErrorNotice
|
|
36
|
+
* key={entry.id}
|
|
37
|
+
* message={entry.error ?? t("Could not save changes")}
|
|
38
|
+
* onRetry={retry}
|
|
39
|
+
* onDismiss={() => dismiss(entry.id)}
|
|
40
|
+
* />
|
|
41
|
+
* ))}
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export function SyncErrorNotice({
|
|
45
|
+
message,
|
|
46
|
+
onRetry,
|
|
47
|
+
onDismiss,
|
|
48
|
+
retryLabel,
|
|
49
|
+
dismissLabel,
|
|
50
|
+
}: SyncErrorNoticeProps) {
|
|
51
|
+
const { t } = useTranslation();
|
|
52
|
+
const { knobProps } = useResolvedKnobs();
|
|
53
|
+
return (
|
|
54
|
+
<XStack
|
|
55
|
+
role="alert"
|
|
56
|
+
alignItems="center"
|
|
57
|
+
flexWrap="wrap"
|
|
58
|
+
gap="$2"
|
|
59
|
+
paddingHorizontal="$3"
|
|
60
|
+
paddingVertical="$2"
|
|
61
|
+
{...knobProps.borderRadius}
|
|
62
|
+
backgroundColor="$red3"
|
|
63
|
+
borderColor="$red7"
|
|
64
|
+
borderWidth={1}
|
|
65
|
+
>
|
|
66
|
+
<Text flex={1} fontSize="$2" color="$red11">
|
|
67
|
+
{message}
|
|
68
|
+
</Text>
|
|
69
|
+
{onRetry && (
|
|
70
|
+
<Button size="$2" theme="error" {...knobProps.borderRadius} onPress={onRetry}>
|
|
71
|
+
{retryLabel ?? t("Retry")}
|
|
72
|
+
</Button>
|
|
73
|
+
)}
|
|
74
|
+
{onDismiss && (
|
|
75
|
+
<Button size="$2" chromeless {...knobProps.borderRadius} onPress={onDismiss}>
|
|
76
|
+
{dismissLabel ?? t("Dismiss")}
|
|
77
|
+
</Button>
|
|
78
|
+
)}
|
|
79
|
+
</XStack>
|
|
80
|
+
);
|
|
81
|
+
}
|