@multiplatform.one/frappe-ui 7.23.2 → 7.24.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/FrappeCalendar.cjs +1 -1
- package/dist/cjs/FrappeCalendar.native.js +1 -1
- package/dist/cjs/FrappeCalendar.native.js.map +1 -1
- package/dist/cjs/FrappeDashboard.cjs +1 -1
- package/dist/cjs/FrappeDashboard.native.js +1 -1
- package/dist/cjs/FrappeDashboard.native.js.map +1 -1
- package/dist/cjs/FrappeImageView.cjs +1 -1
- package/dist/cjs/FrappeImageView.native.js +1 -1
- package/dist/cjs/FrappeImageView.native.js.map +1 -1
- package/dist/cjs/FrappeInsights.cjs +380 -0
- package/dist/cjs/FrappeInsights.native.js +447 -0
- package/dist/cjs/FrappeInsights.native.js.map +1 -0
- package/dist/cjs/FrappeLikedBy.cjs +5 -2
- package/dist/cjs/FrappeLikedBy.native.js +5 -2
- package/dist/cjs/FrappeLikedBy.native.js.map +1 -1
- package/dist/cjs/FrappeListView.cjs +392 -38
- package/dist/cjs/FrappeListView.native.js +454 -41
- package/dist/cjs/FrappeListView.native.js.map +1 -1
- package/dist/cjs/FrappeRecordView.cjs +199 -0
- package/dist/cjs/FrappeRecordView.native.js +218 -0
- package/dist/cjs/FrappeRecordView.native.js.map +1 -0
- 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/FrappeTreeView.cjs +1 -1
- package/dist/cjs/FrappeTreeView.native.js +1 -1
- package/dist/cjs/desk/awesomebar.cjs +1 -1
- package/dist/cjs/desk/awesomebar.native.js +3 -3
- package/dist/cjs/desk/awesomebar.native.js.map +1 -1
- package/dist/cjs/fields/Heading/index.cjs +18 -11
- package/dist/cjs/fields/Heading/index.native.js +21 -12
- package/dist/cjs/fields/Heading/index.native.js.map +1 -1
- package/dist/cjs/frappeNavigation.cjs +12 -2
- package/dist/cjs/frappeNavigation.native.js +13 -2
- package/dist/cjs/frappeNavigation.native.js.map +1 -1
- package/dist/cjs/index.cjs +34 -0
- package/dist/cjs/index.native.js +34 -0
- package/dist/cjs/index.native.js.map +1 -1
- package/dist/cjs/insights/InsightsFacetTags.cjs +147 -0
- package/dist/cjs/insights/InsightsFacetTags.native.js +165 -0
- package/dist/cjs/insights/InsightsFacetTags.native.js.map +1 -0
- package/dist/cjs/insights/InsightsKpiBand.cjs +124 -0
- package/dist/cjs/insights/InsightsKpiBand.native.js +133 -0
- package/dist/cjs/insights/InsightsKpiBand.native.js.map +1 -0
- package/dist/cjs/insights/InsightsLeaders.cjs +150 -0
- package/dist/cjs/insights/InsightsLeaders.native.js +178 -0
- package/dist/cjs/insights/InsightsLeaders.native.js.map +1 -0
- package/dist/cjs/insights/InsightsRecords.cjs +105 -0
- package/dist/cjs/insights/InsightsRecords.native.js +109 -0
- package/dist/cjs/insights/InsightsRecords.native.js.map +1 -0
- package/dist/cjs/insights/InsightsTable.cjs +157 -0
- package/dist/cjs/insights/InsightsTable.native.js +194 -0
- package/dist/cjs/insights/InsightsTable.native.js.map +1 -0
- package/dist/cjs/insights/insightsData.cjs +114 -0
- package/dist/cjs/insights/insightsData.native.js +220 -0
- package/dist/cjs/insights/insightsData.native.js.map +1 -0
- package/dist/cjs/insights/insightsRecord.cjs +35 -0
- package/dist/cjs/insights/insightsRecord.native.js +38 -0
- package/dist/cjs/insights/insightsRecord.native.js.map +1 -0
- package/dist/cjs/list/FacetBadge.cjs +62 -0
- package/dist/cjs/list/FacetBadge.native.js +66 -0
- package/dist/cjs/list/FacetBadge.native.js.map +1 -0
- package/dist/cjs/list/FacetRail.cjs +199 -0
- package/dist/cjs/list/FacetRail.native.js +223 -0
- package/dist/cjs/list/FacetRail.native.js.map +1 -0
- package/dist/cjs/list/ImageWell.cjs +111 -0
- package/dist/cjs/list/ImageWell.native.js +126 -0
- package/dist/cjs/list/ImageWell.native.js.map +1 -0
- package/dist/cjs/list/ListControls.cjs +226 -0
- package/dist/cjs/list/ListControls.native.js +252 -0
- package/dist/cjs/list/ListControls.native.js.map +1 -0
- package/dist/cjs/list/ListToastHost.cjs +54 -0
- package/dist/cjs/list/ListToastHost.native.js +59 -0
- package/dist/cjs/list/ListToastHost.native.js.map +1 -0
- package/dist/cjs/list/RangeFilter.cjs +122 -0
- package/dist/cjs/list/RangeFilter.native.js +135 -0
- package/dist/cjs/list/RangeFilter.native.js.map +1 -0
- package/dist/cjs/list/RecordCard.cjs +233 -0
- package/dist/cjs/list/RecordCard.native.js +173 -0
- package/dist/cjs/list/RecordCard.native.js.map +1 -0
- package/dist/cjs/list/ShufflePanel.cjs +112 -0
- package/dist/cjs/list/ShufflePanel.native.js +139 -0
- package/dist/cjs/list/ShufflePanel.native.js.map +1 -0
- package/dist/cjs/list/facetReading.cjs +71 -0
- package/dist/cjs/list/facetReading.native.js +163 -0
- package/dist/cjs/list/facetReading.native.js.map +1 -0
- package/dist/cjs/list/favorites.cjs +79 -0
- package/dist/cjs/list/favorites.native.js +91 -0
- package/dist/cjs/list/favorites.native.js.map +1 -0
- package/dist/cjs/list/listState.cjs +100 -0
- package/dist/cjs/list/listState.native.js +234 -0
- package/dist/cjs/list/listState.native.js.map +1 -0
- package/dist/cjs/list/recordLook.cjs +35 -0
- package/dist/cjs/list/recordLook.native.js +39 -0
- package/dist/cjs/list/recordLook.native.js.map +1 -0
- package/dist/cjs/list/useListControls.cjs +189 -0
- package/dist/cjs/list/useListControls.native.js +272 -0
- package/dist/cjs/list/useListControls.native.js.map +1 -0
- package/dist/cjs/record/RecordBars.cjs +200 -0
- package/dist/cjs/record/RecordBars.native.js +225 -0
- package/dist/cjs/record/RecordBars.native.js.map +1 -0
- package/dist/cjs/record/RecordGallery.cjs +67 -0
- package/dist/cjs/record/RecordGallery.native.js +75 -0
- package/dist/cjs/record/RecordGallery.native.js.map +1 -0
- package/dist/cjs/record/RecordHero.cjs +239 -0
- package/dist/cjs/record/RecordHero.native.js +250 -0
- package/dist/cjs/record/RecordHero.native.js.map +1 -0
- package/dist/cjs/record/RecordPage.cjs +330 -0
- package/dist/cjs/record/RecordPage.native.js +364 -0
- package/dist/cjs/record/RecordPage.native.js.map +1 -0
- package/dist/cjs/record/RecordPager.cjs +125 -0
- package/dist/cjs/record/RecordPager.native.js +130 -0
- package/dist/cjs/record/RecordPager.native.js.map +1 -0
- package/dist/cjs/record/RecordRows.cjs +126 -0
- package/dist/cjs/record/RecordRows.native.js +140 -0
- package/dist/cjs/record/RecordRows.native.js.map +1 -0
- package/dist/cjs/record/recordData.cjs +122 -0
- package/dist/cjs/record/recordData.native.js +189 -0
- package/dist/cjs/record/recordData.native.js.map +1 -0
- package/dist/cjs/record/recordSections.cjs +30 -0
- package/dist/cjs/record/recordSections.native.js +33 -0
- package/dist/cjs/record/recordSections.native.js.map +1 -0
- package/dist/esm/FrappeCalendar.mjs +1 -1
- package/dist/esm/FrappeCalendar.mjs.map +1 -1
- package/dist/esm/FrappeCalendar.native.js +1 -1
- package/dist/esm/FrappeCalendar.native.js.map +1 -1
- package/dist/esm/FrappeDashboard.mjs +1 -1
- package/dist/esm/FrappeDashboard.mjs.map +1 -1
- package/dist/esm/FrappeDashboard.native.js +1 -1
- package/dist/esm/FrappeDashboard.native.js.map +1 -1
- package/dist/esm/FrappeImageView.mjs +1 -1
- package/dist/esm/FrappeImageView.mjs.map +1 -1
- package/dist/esm/FrappeImageView.native.js +1 -1
- package/dist/esm/FrappeImageView.native.js.map +1 -1
- package/dist/esm/FrappeInsights.mjs +355 -0
- package/dist/esm/FrappeInsights.mjs.map +1 -0
- package/dist/esm/FrappeInsights.native.js +419 -0
- package/dist/esm/FrappeInsights.native.js.map +1 -0
- package/dist/esm/FrappeLikedBy.mjs +6 -3
- package/dist/esm/FrappeLikedBy.mjs.map +1 -1
- package/dist/esm/FrappeLikedBy.native.js +6 -3
- package/dist/esm/FrappeLikedBy.native.js.map +1 -1
- package/dist/esm/FrappeListView.mjs +396 -42
- package/dist/esm/FrappeListView.mjs.map +1 -1
- package/dist/esm/FrappeListView.native.js +458 -45
- package/dist/esm/FrappeListView.native.js.map +1 -1
- package/dist/esm/FrappeRecordView.mjs +174 -0
- package/dist/esm/FrappeRecordView.mjs.map +1 -0
- package/dist/esm/FrappeRecordView.native.js +190 -0
- package/dist/esm/FrappeRecordView.native.js.map +1 -0
- 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/FrappeTreeView.mjs +1 -1
- package/dist/esm/FrappeTreeView.mjs.map +1 -1
- package/dist/esm/FrappeTreeView.native.js +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 +3 -3
- package/dist/esm/desk/awesomebar.native.js.map +1 -1
- package/dist/esm/fields/Heading/index.mjs +19 -12
- package/dist/esm/fields/Heading/index.mjs.map +1 -1
- package/dist/esm/fields/Heading/index.native.js +22 -13
- package/dist/esm/fields/Heading/index.native.js.map +1 -1
- package/dist/esm/frappeNavigation.mjs +11 -2
- package/dist/esm/frappeNavigation.mjs.map +1 -1
- package/dist/esm/frappeNavigation.native.js +12 -2
- package/dist/esm/frappeNavigation.native.js.map +1 -1
- package/dist/esm/index.mjs +14 -2
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/index.native.js +14 -2
- package/dist/esm/index.native.js.map +1 -1
- package/dist/esm/insights/InsightsFacetTags.mjs +122 -0
- package/dist/esm/insights/InsightsFacetTags.mjs.map +1 -0
- package/dist/esm/insights/InsightsFacetTags.native.js +137 -0
- package/dist/esm/insights/InsightsFacetTags.native.js.map +1 -0
- package/dist/esm/insights/InsightsKpiBand.mjs +99 -0
- package/dist/esm/insights/InsightsKpiBand.mjs.map +1 -0
- package/dist/esm/insights/InsightsKpiBand.native.js +105 -0
- package/dist/esm/insights/InsightsKpiBand.native.js.map +1 -0
- package/dist/esm/insights/InsightsLeaders.mjs +125 -0
- package/dist/esm/insights/InsightsLeaders.mjs.map +1 -0
- package/dist/esm/insights/InsightsLeaders.native.js +150 -0
- package/dist/esm/insights/InsightsLeaders.native.js.map +1 -0
- package/dist/esm/insights/InsightsRecords.mjs +80 -0
- package/dist/esm/insights/InsightsRecords.mjs.map +1 -0
- package/dist/esm/insights/InsightsRecords.native.js +81 -0
- package/dist/esm/insights/InsightsRecords.native.js.map +1 -0
- package/dist/esm/insights/InsightsTable.mjs +132 -0
- package/dist/esm/insights/InsightsTable.mjs.map +1 -0
- package/dist/esm/insights/InsightsTable.native.js +166 -0
- package/dist/esm/insights/InsightsTable.native.js.map +1 -0
- package/dist/esm/insights/insightsData.mjs +82 -0
- package/dist/esm/insights/insightsData.mjs.map +1 -0
- package/dist/esm/insights/insightsData.native.js +185 -0
- package/dist/esm/insights/insightsData.native.js.map +1 -0
- package/dist/esm/insights/insightsRecord.mjs +9 -0
- package/dist/esm/insights/insightsRecord.mjs.map +1 -0
- package/dist/esm/insights/insightsRecord.native.js +9 -0
- package/dist/esm/insights/insightsRecord.native.js.map +1 -0
- package/dist/esm/list/FacetBadge.mjs +37 -0
- package/dist/esm/list/FacetBadge.mjs.map +1 -0
- package/dist/esm/list/FacetBadge.native.js +38 -0
- package/dist/esm/list/FacetBadge.native.js.map +1 -0
- package/dist/esm/list/FacetRail.mjs +174 -0
- package/dist/esm/list/FacetRail.mjs.map +1 -0
- package/dist/esm/list/FacetRail.native.js +195 -0
- package/dist/esm/list/FacetRail.native.js.map +1 -0
- package/dist/esm/list/ImageWell.mjs +86 -0
- package/dist/esm/list/ImageWell.mjs.map +1 -0
- package/dist/esm/list/ImageWell.native.js +98 -0
- package/dist/esm/list/ImageWell.native.js.map +1 -0
- package/dist/esm/list/ListControls.mjs +201 -0
- package/dist/esm/list/ListControls.mjs.map +1 -0
- package/dist/esm/list/ListControls.native.js +224 -0
- package/dist/esm/list/ListControls.native.js.map +1 -0
- package/dist/esm/list/ListToastHost.mjs +18 -0
- package/dist/esm/list/ListToastHost.mjs.map +1 -0
- package/dist/esm/list/ListToastHost.native.js +20 -0
- package/dist/esm/list/ListToastHost.native.js.map +1 -0
- package/dist/esm/list/RangeFilter.mjs +85 -0
- package/dist/esm/list/RangeFilter.mjs.map +1 -0
- package/dist/esm/list/RangeFilter.native.js +95 -0
- package/dist/esm/list/RangeFilter.native.js.map +1 -0
- package/dist/esm/list/RecordCard.mjs +208 -0
- package/dist/esm/list/RecordCard.mjs.map +1 -0
- package/dist/esm/list/RecordCard.native.js +145 -0
- package/dist/esm/list/RecordCard.native.js.map +1 -0
- package/dist/esm/list/ShufflePanel.mjs +87 -0
- package/dist/esm/list/ShufflePanel.mjs.map +1 -0
- package/dist/esm/list/ShufflePanel.native.js +111 -0
- package/dist/esm/list/ShufflePanel.native.js.map +1 -0
- package/dist/esm/list/facetReading.mjs +45 -0
- package/dist/esm/list/facetReading.mjs.map +1 -0
- package/dist/esm/list/facetReading.native.js +134 -0
- package/dist/esm/list/facetReading.native.js.map +1 -0
- package/dist/esm/list/favorites.mjs +50 -0
- package/dist/esm/list/favorites.mjs.map +1 -0
- package/dist/esm/list/favorites.native.js +59 -0
- package/dist/esm/list/favorites.native.js.map +1 -0
- package/dist/esm/list/listState.mjs +71 -0
- package/dist/esm/list/listState.mjs.map +1 -0
- package/dist/esm/list/listState.native.js +202 -0
- package/dist/esm/list/listState.native.js.map +1 -0
- package/dist/esm/list/recordLook.mjs +10 -0
- package/dist/esm/list/recordLook.mjs.map +1 -0
- package/dist/esm/list/recordLook.native.js +11 -0
- package/dist/esm/list/recordLook.native.js.map +1 -0
- package/dist/esm/list/useListControls.mjs +164 -0
- package/dist/esm/list/useListControls.mjs.map +1 -0
- package/dist/esm/list/useListControls.native.js +244 -0
- package/dist/esm/list/useListControls.native.js.map +1 -0
- package/dist/esm/record/RecordBars.mjs +175 -0
- package/dist/esm/record/RecordBars.mjs.map +1 -0
- package/dist/esm/record/RecordBars.native.js +197 -0
- package/dist/esm/record/RecordBars.native.js.map +1 -0
- package/dist/esm/record/RecordGallery.mjs +42 -0
- package/dist/esm/record/RecordGallery.mjs.map +1 -0
- package/dist/esm/record/RecordGallery.native.js +47 -0
- package/dist/esm/record/RecordGallery.native.js.map +1 -0
- package/dist/esm/record/RecordHero.mjs +205 -0
- package/dist/esm/record/RecordHero.mjs.map +1 -0
- package/dist/esm/record/RecordHero.native.js +213 -0
- package/dist/esm/record/RecordHero.native.js.map +1 -0
- package/dist/esm/record/RecordPage.mjs +305 -0
- package/dist/esm/record/RecordPage.mjs.map +1 -0
- package/dist/esm/record/RecordPage.native.js +336 -0
- package/dist/esm/record/RecordPage.native.js.map +1 -0
- package/dist/esm/record/RecordPager.mjs +100 -0
- package/dist/esm/record/RecordPager.mjs.map +1 -0
- package/dist/esm/record/RecordPager.native.js +102 -0
- package/dist/esm/record/RecordPager.native.js.map +1 -0
- package/dist/esm/record/RecordRows.mjs +101 -0
- package/dist/esm/record/RecordRows.mjs.map +1 -0
- package/dist/esm/record/RecordRows.native.js +112 -0
- package/dist/esm/record/RecordRows.native.js.map +1 -0
- package/dist/esm/record/recordData.mjs +92 -0
- package/dist/esm/record/recordData.mjs.map +1 -0
- package/dist/esm/record/recordData.native.js +156 -0
- package/dist/esm/record/recordData.native.js.map +1 -0
- package/dist/esm/record/recordSections.mjs +5 -0
- package/dist/esm/record/recordSections.mjs.map +1 -0
- package/dist/esm/record/recordSections.native.js +5 -0
- package/dist/esm/record/recordSections.native.js.map +1 -0
- package/dist/jsx/FrappeCalendar.mjs +1 -1
- package/dist/jsx/FrappeCalendar.mjs.map +1 -1
- package/dist/jsx/FrappeCalendar.native.js +1 -1
- package/dist/jsx/FrappeCalendar.native.js.map +1 -1
- package/dist/jsx/FrappeDashboard.mjs +1 -1
- package/dist/jsx/FrappeDashboard.mjs.map +1 -1
- package/dist/jsx/FrappeDashboard.native.js +1 -1
- package/dist/jsx/FrappeDashboard.native.js.map +1 -1
- package/dist/jsx/FrappeImageView.mjs +1 -1
- package/dist/jsx/FrappeImageView.mjs.map +1 -1
- package/dist/jsx/FrappeImageView.native.js +1 -1
- package/dist/jsx/FrappeImageView.native.js.map +1 -1
- package/dist/jsx/FrappeInsights.mjs +355 -0
- package/dist/jsx/FrappeInsights.mjs.map +1 -0
- package/dist/jsx/FrappeInsights.native.js +447 -0
- package/dist/jsx/FrappeInsights.native.js.map +1 -0
- package/dist/jsx/FrappeLikedBy.mjs +6 -3
- package/dist/jsx/FrappeLikedBy.mjs.map +1 -1
- package/dist/jsx/FrappeLikedBy.native.js +5 -2
- package/dist/jsx/FrappeLikedBy.native.js.map +1 -1
- package/dist/jsx/FrappeListView.mjs +396 -42
- package/dist/jsx/FrappeListView.mjs.map +1 -1
- package/dist/jsx/FrappeListView.native.js +454 -41
- package/dist/jsx/FrappeListView.native.js.map +1 -1
- package/dist/jsx/FrappeRecordView.mjs +174 -0
- package/dist/jsx/FrappeRecordView.mjs.map +1 -0
- package/dist/jsx/FrappeRecordView.native.js +218 -0
- package/dist/jsx/FrappeRecordView.native.js.map +1 -0
- 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/FrappeTreeView.mjs +1 -1
- package/dist/jsx/FrappeTreeView.mjs.map +1 -1
- package/dist/jsx/FrappeTreeView.native.js +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 +3 -3
- package/dist/jsx/desk/awesomebar.native.js.map +1 -1
- package/dist/jsx/fields/Heading/index.mjs +19 -12
- package/dist/jsx/fields/Heading/index.mjs.map +1 -1
- package/dist/jsx/fields/Heading/index.native.js +21 -12
- package/dist/jsx/fields/Heading/index.native.js.map +1 -1
- package/dist/jsx/frappeNavigation.mjs +11 -2
- package/dist/jsx/frappeNavigation.mjs.map +1 -1
- package/dist/jsx/frappeNavigation.native.js +13 -2
- package/dist/jsx/frappeNavigation.native.js.map +1 -1
- package/dist/jsx/index.js +14 -2
- package/dist/jsx/index.js.map +1 -1
- package/dist/jsx/index.mjs +14 -2
- package/dist/jsx/index.mjs.map +1 -1
- package/dist/jsx/index.native.js +34 -0
- package/dist/jsx/index.native.js.map +1 -1
- package/dist/jsx/insights/InsightsFacetTags.mjs +122 -0
- package/dist/jsx/insights/InsightsFacetTags.mjs.map +1 -0
- package/dist/jsx/insights/InsightsFacetTags.native.js +165 -0
- package/dist/jsx/insights/InsightsFacetTags.native.js.map +1 -0
- package/dist/jsx/insights/InsightsKpiBand.mjs +99 -0
- package/dist/jsx/insights/InsightsKpiBand.mjs.map +1 -0
- package/dist/jsx/insights/InsightsKpiBand.native.js +133 -0
- package/dist/jsx/insights/InsightsKpiBand.native.js.map +1 -0
- package/dist/jsx/insights/InsightsLeaders.mjs +125 -0
- package/dist/jsx/insights/InsightsLeaders.mjs.map +1 -0
- package/dist/jsx/insights/InsightsLeaders.native.js +178 -0
- package/dist/jsx/insights/InsightsLeaders.native.js.map +1 -0
- package/dist/jsx/insights/InsightsRecords.mjs +80 -0
- package/dist/jsx/insights/InsightsRecords.mjs.map +1 -0
- package/dist/jsx/insights/InsightsRecords.native.js +109 -0
- package/dist/jsx/insights/InsightsRecords.native.js.map +1 -0
- package/dist/jsx/insights/InsightsTable.mjs +132 -0
- package/dist/jsx/insights/InsightsTable.mjs.map +1 -0
- package/dist/jsx/insights/InsightsTable.native.js +194 -0
- package/dist/jsx/insights/InsightsTable.native.js.map +1 -0
- package/dist/jsx/insights/insightsData.mjs +82 -0
- package/dist/jsx/insights/insightsData.mjs.map +1 -0
- package/dist/jsx/insights/insightsData.native.js +220 -0
- package/dist/jsx/insights/insightsData.native.js.map +1 -0
- package/dist/jsx/insights/insightsRecord.mjs +9 -0
- package/dist/jsx/insights/insightsRecord.mjs.map +1 -0
- package/dist/jsx/insights/insightsRecord.native.js +38 -0
- package/dist/jsx/insights/insightsRecord.native.js.map +1 -0
- package/dist/jsx/list/FacetBadge.mjs +37 -0
- package/dist/jsx/list/FacetBadge.mjs.map +1 -0
- package/dist/jsx/list/FacetBadge.native.js +66 -0
- package/dist/jsx/list/FacetBadge.native.js.map +1 -0
- package/dist/jsx/list/FacetRail.mjs +174 -0
- package/dist/jsx/list/FacetRail.mjs.map +1 -0
- package/dist/jsx/list/FacetRail.native.js +223 -0
- package/dist/jsx/list/FacetRail.native.js.map +1 -0
- package/dist/jsx/list/ImageWell.mjs +86 -0
- package/dist/jsx/list/ImageWell.mjs.map +1 -0
- package/dist/jsx/list/ImageWell.native.js +126 -0
- package/dist/jsx/list/ImageWell.native.js.map +1 -0
- package/dist/jsx/list/ListControls.mjs +201 -0
- package/dist/jsx/list/ListControls.mjs.map +1 -0
- package/dist/jsx/list/ListControls.native.js +252 -0
- package/dist/jsx/list/ListControls.native.js.map +1 -0
- package/dist/jsx/list/ListToastHost.mjs +18 -0
- package/dist/jsx/list/ListToastHost.mjs.map +1 -0
- package/dist/jsx/list/ListToastHost.native.js +59 -0
- package/dist/jsx/list/ListToastHost.native.js.map +1 -0
- package/dist/jsx/list/RangeFilter.mjs +85 -0
- package/dist/jsx/list/RangeFilter.mjs.map +1 -0
- package/dist/jsx/list/RangeFilter.native.js +135 -0
- package/dist/jsx/list/RangeFilter.native.js.map +1 -0
- package/dist/jsx/list/RecordCard.mjs +208 -0
- package/dist/jsx/list/RecordCard.mjs.map +1 -0
- package/dist/jsx/list/RecordCard.native.js +173 -0
- package/dist/jsx/list/RecordCard.native.js.map +1 -0
- package/dist/jsx/list/ShufflePanel.mjs +87 -0
- package/dist/jsx/list/ShufflePanel.mjs.map +1 -0
- package/dist/jsx/list/ShufflePanel.native.js +139 -0
- package/dist/jsx/list/ShufflePanel.native.js.map +1 -0
- package/dist/jsx/list/facetReading.mjs +45 -0
- package/dist/jsx/list/facetReading.mjs.map +1 -0
- package/dist/jsx/list/facetReading.native.js +163 -0
- package/dist/jsx/list/facetReading.native.js.map +1 -0
- package/dist/jsx/list/favorites.mjs +50 -0
- package/dist/jsx/list/favorites.mjs.map +1 -0
- package/dist/jsx/list/favorites.native.js +91 -0
- package/dist/jsx/list/favorites.native.js.map +1 -0
- package/dist/jsx/list/listState.mjs +71 -0
- package/dist/jsx/list/listState.mjs.map +1 -0
- package/dist/jsx/list/listState.native.js +234 -0
- package/dist/jsx/list/listState.native.js.map +1 -0
- package/dist/jsx/list/recordLook.mjs +10 -0
- package/dist/jsx/list/recordLook.mjs.map +1 -0
- package/dist/jsx/list/recordLook.native.js +39 -0
- package/dist/jsx/list/recordLook.native.js.map +1 -0
- package/dist/jsx/list/useListControls.mjs +164 -0
- package/dist/jsx/list/useListControls.mjs.map +1 -0
- package/dist/jsx/list/useListControls.native.js +272 -0
- package/dist/jsx/list/useListControls.native.js.map +1 -0
- package/dist/jsx/record/RecordBars.mjs +175 -0
- package/dist/jsx/record/RecordBars.mjs.map +1 -0
- package/dist/jsx/record/RecordBars.native.js +225 -0
- package/dist/jsx/record/RecordBars.native.js.map +1 -0
- package/dist/jsx/record/RecordGallery.mjs +42 -0
- package/dist/jsx/record/RecordGallery.mjs.map +1 -0
- package/dist/jsx/record/RecordGallery.native.js +75 -0
- package/dist/jsx/record/RecordGallery.native.js.map +1 -0
- package/dist/jsx/record/RecordHero.mjs +205 -0
- package/dist/jsx/record/RecordHero.mjs.map +1 -0
- package/dist/jsx/record/RecordHero.native.js +250 -0
- package/dist/jsx/record/RecordHero.native.js.map +1 -0
- package/dist/jsx/record/RecordPage.mjs +305 -0
- package/dist/jsx/record/RecordPage.mjs.map +1 -0
- package/dist/jsx/record/RecordPage.native.js +364 -0
- package/dist/jsx/record/RecordPage.native.js.map +1 -0
- package/dist/jsx/record/RecordPager.mjs +100 -0
- package/dist/jsx/record/RecordPager.mjs.map +1 -0
- package/dist/jsx/record/RecordPager.native.js +130 -0
- package/dist/jsx/record/RecordPager.native.js.map +1 -0
- package/dist/jsx/record/RecordRows.mjs +101 -0
- package/dist/jsx/record/RecordRows.mjs.map +1 -0
- package/dist/jsx/record/RecordRows.native.js +140 -0
- package/dist/jsx/record/RecordRows.native.js.map +1 -0
- package/dist/jsx/record/recordData.mjs +92 -0
- package/dist/jsx/record/recordData.mjs.map +1 -0
- package/dist/jsx/record/recordData.native.js +189 -0
- package/dist/jsx/record/recordData.native.js.map +1 -0
- package/dist/jsx/record/recordSections.mjs +5 -0
- package/dist/jsx/record/recordSections.mjs.map +1 -0
- package/dist/jsx/record/recordSections.native.js +33 -0
- package/dist/jsx/record/recordSections.native.js.map +1 -0
- package/package.json +16 -16
- package/src/FrappeCalendar.tsx +2 -2
- package/src/FrappeDashboard.tsx +2 -2
- package/src/FrappeImageView.tsx +2 -1
- package/src/FrappeInsights.spec.tsx +171 -0
- package/src/FrappeInsights.tsx +422 -0
- package/src/FrappeLikedBy.tsx +7 -5
- package/src/FrappeListView.card.spec.tsx +157 -0
- package/src/FrappeListView.controls.spec.tsx +225 -0
- package/src/FrappeListView.seed.spec.tsx +30 -0
- package/src/FrappeListView.tsx +575 -44
- package/src/FrappeRecordView.spec.tsx +276 -0
- package/src/FrappeRecordView.tsx +231 -0
- package/src/FrappeReportBuilder.tsx +4 -5
- package/src/FrappeTreeView.tsx +1 -1
- package/src/desk/awesomebar.auth.spec.tsx +77 -0
- package/src/desk/awesomebar.tsx +1 -1
- package/src/fields/Heading/index.tsx +33 -15
- package/src/frappeNavigation.loader.spec.tsx +45 -0
- package/src/frappeNavigation.tsx +32 -1
- package/src/index.ts +53 -1
- package/src/insights/InsightsFacetTags.tsx +131 -0
- package/src/insights/InsightsKpiBand.tsx +114 -0
- package/src/insights/InsightsLeaders.tsx +146 -0
- package/src/insights/InsightsRecords.tsx +71 -0
- package/src/insights/InsightsTable.tsx +169 -0
- package/src/insights/insightsData.spec.ts +143 -0
- package/src/insights/insightsData.ts +122 -0
- package/src/insights/insightsRecord.ts +36 -0
- package/src/list/FacetBadge.tsx +41 -0
- package/src/list/FacetRail.tsx +235 -0
- package/src/list/ImageWell.tsx +110 -0
- package/src/list/ListControls.tsx +208 -0
- package/src/list/ListToastHost.tsx +27 -0
- package/src/list/RangeFilter.tsx +83 -0
- package/src/list/RecordCard.native.tsx +126 -0
- package/src/list/RecordCard.tsx +260 -0
- package/src/list/ShufflePanel.tsx +107 -0
- package/src/list/facetReading.ts +79 -0
- package/src/list/favorites.spec.ts +80 -0
- package/src/list/favorites.ts +79 -0
- package/src/list/listState.spec.ts +156 -0
- package/src/list/listState.ts +184 -0
- package/src/list/recordLook.ts +37 -0
- package/src/list/useListControls.tsx +223 -0
- package/src/record/RecordBars.tsx +187 -0
- package/src/record/RecordGallery.tsx +47 -0
- package/src/record/RecordHero.tsx +186 -0
- package/src/record/RecordPage.tsx +358 -0
- package/src/record/RecordPager.tsx +97 -0
- package/src/record/RecordRows.tsx +90 -0
- package/src/record/recordData.spec.ts +148 -0
- package/src/record/recordData.ts +124 -0
- package/src/record/recordSections.ts +62 -0
- package/src/schemaForm/SchemaForm.stories.tsx +2 -2
- package/types/FrappeImageView.d.ts.map +1 -1
- package/types/FrappeInsights.d.ts +55 -0
- package/types/FrappeInsights.d.ts.map +1 -0
- package/types/FrappeLikedBy.d.ts.map +1 -1
- package/types/FrappeListView.d.ts +82 -2
- package/types/FrappeListView.d.ts.map +1 -1
- package/types/FrappeRecordView.d.ts +69 -0
- package/types/FrappeRecordView.d.ts.map +1 -0
- package/types/FrappeReportBuilder.d.ts.map +1 -1
- package/types/fields/Heading/index.d.ts.map +1 -1
- package/types/frappeNavigation.d.ts +21 -0
- package/types/frappeNavigation.d.ts.map +1 -1
- package/types/index.d.ts +14 -2
- package/types/index.d.ts.map +1 -1
- package/types/insights/InsightsFacetTags.d.ts +11 -0
- package/types/insights/InsightsFacetTags.d.ts.map +1 -0
- package/types/insights/InsightsKpiBand.d.ts +22 -0
- package/types/insights/InsightsKpiBand.d.ts.map +1 -0
- package/types/insights/InsightsLeaders.d.ts +16 -0
- package/types/insights/InsightsLeaders.d.ts.map +1 -0
- package/types/insights/InsightsRecords.d.ts +19 -0
- package/types/insights/InsightsRecords.d.ts.map +1 -0
- package/types/insights/InsightsTable.d.ts +17 -0
- package/types/insights/InsightsTable.d.ts.map +1 -0
- package/types/insights/insightsData.d.ts +45 -0
- package/types/insights/insightsData.d.ts.map +1 -0
- package/types/insights/insightsRecord.d.ts +29 -0
- package/types/insights/insightsRecord.d.ts.map +1 -0
- package/types/list/FacetBadge.d.ts +13 -0
- package/types/list/FacetBadge.d.ts.map +1 -0
- package/types/list/FacetRail.d.ts +26 -0
- package/types/list/FacetRail.d.ts.map +1 -0
- package/types/list/ImageWell.d.ts +24 -0
- package/types/list/ImageWell.d.ts.map +1 -0
- package/types/list/ListControls.d.ts +30 -0
- package/types/list/ListControls.d.ts.map +1 -0
- package/types/list/ListToastHost.d.ts +7 -0
- package/types/list/ListToastHost.d.ts.map +1 -0
- package/types/list/RangeFilter.d.ts +14 -0
- package/types/list/RangeFilter.d.ts.map +1 -0
- package/types/list/RecordCard.d.ts +38 -0
- package/types/list/RecordCard.d.ts.map +1 -0
- package/types/list/RecordCard.native.d.ts +9 -0
- package/types/list/RecordCard.native.d.ts.map +1 -0
- package/types/list/ShufflePanel.d.ts +24 -0
- package/types/list/ShufflePanel.d.ts.map +1 -0
- package/types/list/facetReading.d.ts +33 -0
- package/types/list/facetReading.d.ts.map +1 -0
- package/types/list/favorites.d.ts +26 -0
- package/types/list/favorites.d.ts.map +1 -0
- package/types/list/listState.d.ts +94 -0
- package/types/list/listState.d.ts.map +1 -0
- package/types/list/recordLook.d.ts +25 -0
- package/types/list/recordLook.d.ts.map +1 -0
- package/types/list/useListControls.d.ts +48 -0
- package/types/list/useListControls.d.ts.map +1 -0
- package/types/record/RecordBars.d.ts +11 -0
- package/types/record/RecordBars.d.ts.map +1 -0
- package/types/record/RecordGallery.d.ts +17 -0
- package/types/record/RecordGallery.d.ts.map +1 -0
- package/types/record/RecordHero.d.ts +42 -0
- package/types/record/RecordHero.d.ts.map +1 -0
- package/types/record/RecordPage.d.ts +54 -0
- package/types/record/RecordPage.d.ts.map +1 -0
- package/types/record/RecordPager.d.ts +13 -0
- package/types/record/RecordPager.d.ts.map +1 -0
- package/types/record/RecordRows.d.ts +13 -0
- package/types/record/RecordRows.d.ts.map +1 -0
- package/types/record/recordData.d.ts +46 -0
- package/types/record/recordData.d.ts.map +1 -0
- package/types/record/recordSections.d.ts +67 -0
- package/types/record/recordSections.d.ts.map +1 -0
package/src/FrappeListView.tsx
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import { HttpClient, useDocTypeMeta } from "@multiplatform.one/frappe";
|
|
2
2
|
import type { DataTableColumn } from "@multiplatform.one/table";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
ArrowsClockwiseIcon,
|
|
5
|
+
HeartIcon,
|
|
6
|
+
ImageIcon,
|
|
7
|
+
ListIcon,
|
|
8
|
+
MagnifyingGlassIcon,
|
|
9
|
+
PlusIcon,
|
|
10
|
+
SparkleIcon,
|
|
11
|
+
TableIcon,
|
|
12
|
+
} from "@phosphor-icons/react";
|
|
4
13
|
import React, { useState, useMemo, useCallback, useEffect } from "react";
|
|
5
14
|
import { useTranslation } from "react-i18next";
|
|
6
15
|
import { Button, SearchInput, Spinner } from "@multiplatform.one/forms";
|
|
@@ -9,10 +18,14 @@ import { SavedFilterDropdown, useSavedFilters, type ParsedFilter } from "@multip
|
|
|
9
18
|
import {
|
|
10
19
|
AsyncBoundary,
|
|
11
20
|
Avatar,
|
|
21
|
+
EmptyState,
|
|
12
22
|
ListPageLayout,
|
|
13
23
|
PageHeader,
|
|
24
|
+
SkipLink,
|
|
25
|
+
Tooltip,
|
|
14
26
|
ViewSwitcher,
|
|
15
27
|
componentColors,
|
|
28
|
+
showToast,
|
|
16
29
|
type ImageGridProps,
|
|
17
30
|
} from "@multiplatform.one/components";
|
|
18
31
|
import { pressTargetStyle, useResolvedKnobs } from "@multiplatform.one/theme";
|
|
@@ -20,11 +33,29 @@ import { storage } from "@multiplatform.one/store";
|
|
|
20
33
|
import { useFrappeConfig } from "./FrappeProvider";
|
|
21
34
|
import { FrappeList, type FrappeListProps } from "./FrappeList";
|
|
22
35
|
import { FrappeNotConfigured } from "./FrappeNotConfigured";
|
|
36
|
+
import { FrappeRowsReader, type FrappeRowsSnapshot } from "./FrappeRowsReader";
|
|
23
37
|
import { FrappeTable } from "./FrappeTable";
|
|
24
38
|
import { useDocRights } from "./formEngine/useDocRights";
|
|
25
|
-
import { frappeRouteHref, useFrappeRouter } from "./frappeNavigation";
|
|
39
|
+
import { frappeRouteHref, useFrappeRouter, useRouteLoaderData } from "./frappeNavigation";
|
|
26
40
|
import { connectionsFromMeta, type FrappeConnection } from "./frappeConnections";
|
|
27
41
|
import { isPermissionError } from "./liveErrors";
|
|
42
|
+
import { favoritesKey } from "./list/favorites";
|
|
43
|
+
import { RecordCard } from "./list/RecordCard";
|
|
44
|
+
import { recordTitle, type FrappeRecordLook } from "./list/recordLook";
|
|
45
|
+
import { ListControls } from "./list/ListControls";
|
|
46
|
+
import {
|
|
47
|
+
isListNarrowed,
|
|
48
|
+
listStateFilters,
|
|
49
|
+
listStateOrderBy,
|
|
50
|
+
type FrappeListControls,
|
|
51
|
+
type FrappeListFacet,
|
|
52
|
+
type FrappeListFilter,
|
|
53
|
+
type FrappeListRange,
|
|
54
|
+
type FrappeListSort,
|
|
55
|
+
} from "./list/listState";
|
|
56
|
+
import { ListToastHost } from "./list/ListToastHost";
|
|
57
|
+
import { ShufflePanel } from "./list/ShufflePanel";
|
|
58
|
+
import { useListControls } from "./list/useListControls";
|
|
28
59
|
import { useConnectionFilters, type FrappeDoctypeFilter } from "./useConnectionFilters";
|
|
29
60
|
|
|
30
61
|
interface ListFieldConfig {
|
|
@@ -35,6 +66,30 @@ interface ListFieldConfig {
|
|
|
35
66
|
|
|
36
67
|
export type ListViewMode = "list" | "table" | "grid";
|
|
37
68
|
|
|
69
|
+
/** What a card needs from the list beyond its row. */
|
|
70
|
+
export interface FrappeListCard {
|
|
71
|
+
/** The record's page, when the list is routed. */
|
|
72
|
+
href?: string;
|
|
73
|
+
/** Opens the record the way a row press does. */
|
|
74
|
+
open: () => void;
|
|
75
|
+
/** Facet param to the record's values (a Pokemon's types, in slot order). */
|
|
76
|
+
facets: Record<string, string[]>;
|
|
77
|
+
/** Present when the list offers favorites. */
|
|
78
|
+
favorite?: { active: boolean; toggle: () => void };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** A page-header action of a routed list; icon-only under sm. */
|
|
82
|
+
export interface FrappeListAction {
|
|
83
|
+
label: string;
|
|
84
|
+
icon?: React.ComponentType<{ size?: number }>;
|
|
85
|
+
/** Opened through the FrappeProvider's navigation. */
|
|
86
|
+
href?: string;
|
|
87
|
+
onPress?: () => void;
|
|
88
|
+
/** Tooltip. */
|
|
89
|
+
hint?: string;
|
|
90
|
+
testID?: string;
|
|
91
|
+
}
|
|
92
|
+
|
|
38
93
|
export interface FrappeListViewProps<TDoc extends { name: string; doctype: string }> extends Omit<
|
|
39
94
|
YStackProps,
|
|
40
95
|
"children"
|
|
@@ -71,14 +126,27 @@ export interface FrappeListViewProps<TDoc extends { name: string; doctype: strin
|
|
|
71
126
|
orderBy?: FrappeListProps<TDoc>["orderBy"];
|
|
72
127
|
limit?: number;
|
|
73
128
|
initialRows?: readonly Partial<TDoc>[];
|
|
129
|
+
/**
|
|
130
|
+
* The route's loader (`frappeListLoader`): the rows it rendered on the
|
|
131
|
+
* server seed the list, read through the host's navigation.
|
|
132
|
+
*/
|
|
133
|
+
loader?: (...args: never[]) => Promise<{ rows: readonly Partial<TDoc>[] }>;
|
|
74
134
|
columns?: DataTableColumn<Partial<TDoc>>[];
|
|
75
135
|
displayFields?: ListFieldConfig[];
|
|
76
136
|
/**
|
|
77
137
|
* Per-row card slot. Given, the view gains a card grid (`grid` mode, the
|
|
78
138
|
* catalog ImageGrid) that draws each row with it; loading, empty and error
|
|
79
|
-
* stay the view's own states.
|
|
139
|
+
* stay the view's own states. A host that draws the grid itself may leave
|
|
140
|
+
* `card` out.
|
|
80
141
|
*/
|
|
81
|
-
renderCard?: (doc: Partial<TDoc>, index: number) => React.ReactNode;
|
|
142
|
+
renderCard?: (doc: Partial<TDoc>, index: number, card?: FrappeListCard) => React.ReactNode;
|
|
143
|
+
/**
|
|
144
|
+
* How the doctype's records present. With `images` and no card slot, the
|
|
145
|
+
* grid draws each record as a RecordCard: the look's title, code,
|
|
146
|
+
* subtitle and images, the well tinted and badged by the first facet that
|
|
147
|
+
* declares tints, the favorite heart when the list keeps favorites.
|
|
148
|
+
*/
|
|
149
|
+
look?: FrappeRecordLook<TDoc>;
|
|
82
150
|
/** Card grid: column count, tile rung and media aspect. */
|
|
83
151
|
gridProps?: Pick<ImageGridProps<Partial<TDoc>>, "columns" | "tileSize" | "mediaAspect">;
|
|
84
152
|
/** Replaces the default empty message in list and card modes. */
|
|
@@ -98,6 +166,40 @@ export interface FrappeListViewProps<TDoc extends { name: string; doctype: strin
|
|
|
98
166
|
showFilterSidebar?: boolean;
|
|
99
167
|
/** @deprecated The toolbar always renders the List/Table switcher now. */
|
|
100
168
|
enableViewSwitcher?: boolean;
|
|
169
|
+
/** Routed page title; defaults to the doctype. */
|
|
170
|
+
title?: string;
|
|
171
|
+
/** Routed page subtitle from the number of records the list reads. */
|
|
172
|
+
subtitle?: (count: number) => string;
|
|
173
|
+
/**
|
|
174
|
+
* Routed page actions. With the toolbar off, the view switcher and Add
|
|
175
|
+
* join them in the page header.
|
|
176
|
+
*/
|
|
177
|
+
actions?: readonly FrappeListAction[];
|
|
178
|
+
/** Header content ahead of the actions. */
|
|
179
|
+
headerAccessory?: React.ReactNode;
|
|
180
|
+
/** The field that names a record in toasts; defaults to meta.title_field. */
|
|
181
|
+
titleField?: string;
|
|
182
|
+
/** Sort choices, first is the default; given, a Sort select joins the controls. */
|
|
183
|
+
sorts?: readonly FrappeListSort[];
|
|
184
|
+
/** The bench filters a search becomes; defaults to a like on the title field. */
|
|
185
|
+
searchFilter?: (query: string) => readonly FrappeListFilter[];
|
|
186
|
+
searchPlaceholder?: string;
|
|
187
|
+
/** One chip rail per facet, with each value's record count. */
|
|
188
|
+
facets?: readonly FrappeListFacet[];
|
|
189
|
+
/** One minimum-value slider per numeric field. */
|
|
190
|
+
ranges?: readonly FrappeListRange[];
|
|
191
|
+
/**
|
|
192
|
+
* Star records on this device and filter to them: true keys the stars by
|
|
193
|
+
* doctype, a string names the storage key.
|
|
194
|
+
*/
|
|
195
|
+
favorites?: boolean | string;
|
|
196
|
+
/** A random-record picker; `label` names a record on its wheel. */
|
|
197
|
+
shuffle?: { label: (doc: Partial<TDoc>) => string };
|
|
198
|
+
/**
|
|
199
|
+
* Keep the controls and the view in the route's query params, so a
|
|
200
|
+
* refresh or a shared link reproduces the list.
|
|
201
|
+
*/
|
|
202
|
+
urlState?: boolean;
|
|
101
203
|
baseURL?: string;
|
|
102
204
|
auth?: { useToken?: boolean; token?: string; type?: string };
|
|
103
205
|
syncOptions?: Record<string, unknown>;
|
|
@@ -105,6 +207,9 @@ export interface FrappeListViewProps<TDoc extends { name: string; doctype: strin
|
|
|
105
207
|
|
|
106
208
|
const storageKeyPrefix = "frappe-listview-";
|
|
107
209
|
|
|
210
|
+
/** Cards whose images load eagerly; the rest wait until they near the viewport. */
|
|
211
|
+
const eagerCards = 6;
|
|
212
|
+
|
|
108
213
|
// Frappe document bookkeeping keys -- never useful as display columns.
|
|
109
214
|
const internalDocKeys = new Set([
|
|
110
215
|
"name",
|
|
@@ -197,10 +302,12 @@ function FrappeListViewBody<TDoc extends { name: string; doctype: string }>({
|
|
|
197
302
|
route,
|
|
198
303
|
orderBy,
|
|
199
304
|
limit,
|
|
200
|
-
initialRows,
|
|
305
|
+
initialRows: initialRowsProp,
|
|
306
|
+
loader,
|
|
201
307
|
columns,
|
|
202
308
|
displayFields,
|
|
203
309
|
renderCard,
|
|
310
|
+
look,
|
|
204
311
|
gridProps,
|
|
205
312
|
emptyState,
|
|
206
313
|
onRetry,
|
|
@@ -211,13 +318,27 @@ function FrappeListViewBody<TDoc extends { name: string; doctype: string }>({
|
|
|
211
318
|
showFilterSidebar = false,
|
|
212
319
|
// deprecated: switcher always renders; destructured so it stays out of stackProps
|
|
213
320
|
enableViewSwitcher: _enableViewSwitcher,
|
|
321
|
+
title,
|
|
322
|
+
subtitle,
|
|
323
|
+
actions,
|
|
324
|
+
headerAccessory,
|
|
325
|
+
titleField,
|
|
326
|
+
sorts,
|
|
327
|
+
searchFilter,
|
|
328
|
+
searchPlaceholder,
|
|
329
|
+
facets,
|
|
330
|
+
ranges,
|
|
331
|
+
favorites,
|
|
332
|
+
shuffle,
|
|
333
|
+
urlState = false,
|
|
214
334
|
baseURL,
|
|
215
335
|
auth,
|
|
216
336
|
syncOptions,
|
|
217
337
|
...stackProps
|
|
218
338
|
}: FrappeListViewProps<TDoc>) {
|
|
219
339
|
const config = useFrappeConfig();
|
|
220
|
-
const
|
|
340
|
+
const router = useFrappeRouter();
|
|
341
|
+
const navigate = router?.navigate;
|
|
221
342
|
const { knobProps } = useResolvedKnobs();
|
|
222
343
|
const { t } = useTranslation();
|
|
223
344
|
const theme = useTheme();
|
|
@@ -242,9 +363,6 @@ function FrappeListViewBody<TDoc extends { name: string; doctype: string }>({
|
|
|
242
363
|
};
|
|
243
364
|
|
|
244
365
|
const [storedViewMode, setViewModeState] = useState<ListViewMode>(defaultViewMode);
|
|
245
|
-
const requestedViewMode = controlledViewMode ?? storedViewMode;
|
|
246
|
-
const viewMode: ListViewMode =
|
|
247
|
-
requestedViewMode === "grid" && !renderCard ? "list" : requestedViewMode;
|
|
248
366
|
|
|
249
367
|
useEffect(() => {
|
|
250
368
|
let cancelled = false;
|
|
@@ -260,16 +378,6 @@ function FrappeListViewBody<TDoc extends { name: string; doctype: string }>({
|
|
|
260
378
|
};
|
|
261
379
|
}, [doctype, defaultViewMode]);
|
|
262
380
|
|
|
263
|
-
const setViewMode = useCallback(
|
|
264
|
-
(mode: ListViewMode) => {
|
|
265
|
-
onViewModeChange?.(mode);
|
|
266
|
-
if (controlledViewMode !== undefined) return;
|
|
267
|
-
setViewModeState(mode);
|
|
268
|
-
void setStoredViewMode(doctype, mode);
|
|
269
|
-
},
|
|
270
|
-
[doctype, controlledViewMode, onViewModeChange],
|
|
271
|
-
);
|
|
272
|
-
|
|
273
381
|
const {
|
|
274
382
|
meta,
|
|
275
383
|
isLoading: isMetaLoading,
|
|
@@ -290,15 +398,6 @@ function FrappeListViewBody<TDoc extends { name: string; doctype: string }>({
|
|
|
290
398
|
(route !== undefined && navigate
|
|
291
399
|
? () => navigate(frappeRouteHref(route, { mode: "create" }))
|
|
292
400
|
: undefined));
|
|
293
|
-
const asPage = (view: React.ReactElement) =>
|
|
294
|
-
route === undefined ? (
|
|
295
|
-
view
|
|
296
|
-
) : (
|
|
297
|
-
<ListPageLayout scroll={false} header={<PageHeader title={doctype} divider={false} />}>
|
|
298
|
-
{view}
|
|
299
|
-
</ListPageLayout>
|
|
300
|
-
);
|
|
301
|
-
|
|
302
401
|
// Guest fallback: DocType meta is permission-gated (Guests get 403) while
|
|
303
402
|
// the documents themselves are often guest-readable. Peek one doc with
|
|
304
403
|
// fields=["*"] and derive display fields from its keys so the views render
|
|
@@ -472,10 +571,108 @@ function FrappeListViewBody<TDoc extends { name: string; doctype: string }>({
|
|
|
472
571
|
() => connections ?? (isMetaLoading ? undefined : connectionsFromMeta(meta)),
|
|
473
572
|
[connections, isMetaLoading, meta],
|
|
474
573
|
);
|
|
574
|
+
|
|
575
|
+
const searchField = useMemo(() => {
|
|
576
|
+
if (meta?.title_field) return meta.title_field;
|
|
577
|
+
if (metaListFields.length > 0) return metaListFields[0].fieldname;
|
|
578
|
+
if (peekedFields && peekedFields.length > 0) return peekedFields[0];
|
|
579
|
+
return "name";
|
|
580
|
+
}, [meta, metaListFields, peekedFields]);
|
|
581
|
+
|
|
582
|
+
// The list's controls (sorts, facets, ranges, favorites, shuffle) own a
|
|
583
|
+
// state the filters and order derive from; a list without them keeps the
|
|
584
|
+
// plain search band below and its own view memory.
|
|
585
|
+
const hasCards = Boolean(renderCard || look?.images);
|
|
586
|
+
const listControls: FrappeListControls = {
|
|
587
|
+
sorts,
|
|
588
|
+
facets,
|
|
589
|
+
ranges,
|
|
590
|
+
views: hasCards ? ["grid", "list", "table"] : ["list", "table"],
|
|
591
|
+
defaultView: defaultViewMode,
|
|
592
|
+
};
|
|
593
|
+
const hasControls = Boolean(
|
|
594
|
+
sorts?.length || facets?.length || ranges?.length || favorites || shuffle,
|
|
595
|
+
);
|
|
596
|
+
const favoritesStorageKey = favorites
|
|
597
|
+
? typeof favorites === "string"
|
|
598
|
+
? favorites
|
|
599
|
+
: favoritesKey(doctype)
|
|
600
|
+
: undefined;
|
|
601
|
+
const list = useListControls({
|
|
602
|
+
doctype,
|
|
603
|
+
controls: listControls,
|
|
604
|
+
connections: effectiveConnections,
|
|
605
|
+
connection: connectionConfig,
|
|
606
|
+
limit,
|
|
607
|
+
favoritesKey: favoritesStorageKey,
|
|
608
|
+
router: urlState ? router : undefined,
|
|
609
|
+
readBase: Boolean(subtitle || facets?.length || ranges?.length),
|
|
610
|
+
});
|
|
611
|
+
const updateList = list.update;
|
|
612
|
+
|
|
613
|
+
const requestedViewMode =
|
|
614
|
+
controlledViewMode ?? (urlState ? list.state.view : undefined) ?? storedViewMode;
|
|
615
|
+
const viewMode: ListViewMode =
|
|
616
|
+
requestedViewMode === "grid" && !hasCards ? "list" : requestedViewMode;
|
|
617
|
+
|
|
618
|
+
const setViewMode = useCallback(
|
|
619
|
+
(mode: ListViewMode) => {
|
|
620
|
+
onViewModeChange?.(mode);
|
|
621
|
+
if (controlledViewMode !== undefined) return;
|
|
622
|
+
if (urlState) {
|
|
623
|
+
updateList({ view: mode });
|
|
624
|
+
return;
|
|
625
|
+
}
|
|
626
|
+
setViewModeState(mode);
|
|
627
|
+
void setStoredViewMode(doctype, mode);
|
|
628
|
+
},
|
|
629
|
+
[doctype, controlledViewMode, onViewModeChange, urlState, updateList],
|
|
630
|
+
);
|
|
631
|
+
|
|
632
|
+
const controlsKey = JSON.stringify({
|
|
633
|
+
facets: facets?.map((facet) => [facet.param, facet.field, facet.doctype]),
|
|
634
|
+
ranges: ranges?.map((range) => [range.param, range.field]),
|
|
635
|
+
sorts: sorts?.map((sort) => [sort.value, sort.orderBy]),
|
|
636
|
+
});
|
|
637
|
+
const controlFilters = useMemo(() => {
|
|
638
|
+
if (!hasControls) return activeFilters;
|
|
639
|
+
const base = !activeFilters
|
|
640
|
+
? []
|
|
641
|
+
: Array.isArray(activeFilters)
|
|
642
|
+
? activeFilters
|
|
643
|
+
: Object.entries(activeFilters).map(([field, value]) => [field, "=", value]);
|
|
644
|
+
return listStateFilters(list.state, listControls, {
|
|
645
|
+
base: base as FrappeListFilter[],
|
|
646
|
+
search: searchFilter ?? ((query) => [[searchField, "like", `%${query}%`]]),
|
|
647
|
+
favorites: list.favorites.favoriteSet,
|
|
648
|
+
});
|
|
649
|
+
// controlsKey stands for the facet and range definitions listControls carries.
|
|
650
|
+
}, [
|
|
651
|
+
hasControls,
|
|
652
|
+
activeFilters,
|
|
653
|
+
list.state,
|
|
654
|
+
list.favorites.favoriteSet,
|
|
655
|
+
searchFilter,
|
|
656
|
+
searchField,
|
|
657
|
+
controlsKey,
|
|
658
|
+
]);
|
|
659
|
+
const effectiveOrderBy = hasControls
|
|
660
|
+
? (listStateOrderBy(list.state, listControls, orderBy) as typeof orderBy)
|
|
661
|
+
: orderBy;
|
|
662
|
+
// The server page is the unfiltered list in its default order; any other
|
|
663
|
+
// query waits for its own read rather than flash rows it would drop.
|
|
664
|
+
const loaded = useRouteLoaderData(loader) as { rows?: readonly Partial<TDoc>[] } | undefined;
|
|
665
|
+
const initialRows = initialRowsProp ?? loaded?.rows;
|
|
666
|
+
const seedRows =
|
|
667
|
+
!hasControls ||
|
|
668
|
+
(!isListNarrowed(list.state) && (!sorts?.length || list.state.sort === sorts[0].value))
|
|
669
|
+
? initialRows
|
|
670
|
+
: undefined;
|
|
671
|
+
|
|
475
672
|
const linkFilters = useConnectionFilters(
|
|
476
673
|
connectionConfig,
|
|
477
674
|
doctype,
|
|
478
|
-
|
|
675
|
+
controlFilters as Parameters<typeof useConnectionFilters>[2],
|
|
479
676
|
effectiveConnections,
|
|
480
677
|
);
|
|
481
678
|
const resolvedFilters = linkFilters.filters as typeof activeFilters;
|
|
@@ -487,13 +684,6 @@ function FrappeListViewBody<TDoc extends { name: string; doctype: string }>({
|
|
|
487
684
|
const [searchText, setSearchText] = useState("");
|
|
488
685
|
const [debouncedSearch, setDebouncedSearch] = useState("");
|
|
489
686
|
|
|
490
|
-
const searchField = useMemo(() => {
|
|
491
|
-
if (meta?.title_field) return meta.title_field;
|
|
492
|
-
if (metaListFields.length > 0) return metaListFields[0].fieldname;
|
|
493
|
-
if (peekedFields && peekedFields.length > 0) return peekedFields[0];
|
|
494
|
-
return "name";
|
|
495
|
-
}, [meta, metaListFields, peekedFields]);
|
|
496
|
-
|
|
497
687
|
const listFilters = useMemo(() => {
|
|
498
688
|
const query = debouncedSearch.trim();
|
|
499
689
|
if (!query) return resolvedFilters;
|
|
@@ -551,6 +741,343 @@ function FrappeListViewBody<TDoc extends { name: string; doctype: string }>({
|
|
|
551
741
|
[savedFiltersHook],
|
|
552
742
|
);
|
|
553
743
|
|
|
744
|
+
const slug = doctype.toLowerCase().replace(/\s+/g, "-");
|
|
745
|
+
const searchId = `${slug}-list-search`;
|
|
746
|
+
const resultsId = `${slug}-results`;
|
|
747
|
+
const nameField = titleField ?? meta?.title_field;
|
|
748
|
+
|
|
749
|
+
const [shuffleOpen, setShuffleOpen] = useState(false);
|
|
750
|
+
const [shuffleRows, setShuffleRows] = useState<Partial<TDoc>[]>([]);
|
|
751
|
+
const handleShuffleRows = useCallback(
|
|
752
|
+
(snapshot: FrappeRowsSnapshot<TDoc>) => setShuffleRows(snapshot.rows),
|
|
753
|
+
[],
|
|
754
|
+
);
|
|
755
|
+
const shuffleOrderKey = JSON.stringify(sorts?.[0]?.orderBy ?? orderBy ?? null);
|
|
756
|
+
const shuffleFiltersKey = JSON.stringify(resolvedFilters ?? null);
|
|
757
|
+
const shuffleFieldsKey = JSON.stringify(effectiveFields);
|
|
758
|
+
const shuffleOptions = useMemo(
|
|
759
|
+
() => ({
|
|
760
|
+
fields: JSON.parse(shuffleFieldsKey),
|
|
761
|
+
filters: JSON.parse(shuffleFiltersKey) ?? undefined,
|
|
762
|
+
orderBy: JSON.parse(shuffleOrderKey) ?? undefined,
|
|
763
|
+
limit: limit ?? 0,
|
|
764
|
+
}),
|
|
765
|
+
[shuffleFieldsKey, shuffleFiltersKey, shuffleOrderKey, limit],
|
|
766
|
+
);
|
|
767
|
+
|
|
768
|
+
const { isFavorite, toggle: toggleStar } = list.favorites;
|
|
769
|
+
const toggleFavorite = useCallback(
|
|
770
|
+
(doc: Partial<TDoc>) => {
|
|
771
|
+
const name = doc.name;
|
|
772
|
+
if (!name) return;
|
|
773
|
+
const starred = isFavorite(name);
|
|
774
|
+
toggleStar(name);
|
|
775
|
+
const titleValue = nameField ? (doc as Record<string, unknown>)[nameField] : undefined;
|
|
776
|
+
const label = typeof titleValue === "string" && titleValue ? titleValue : name;
|
|
777
|
+
showToast(
|
|
778
|
+
starred
|
|
779
|
+
? {
|
|
780
|
+
id: `${slug}-favorite-${name}`,
|
|
781
|
+
title: t("{{name}} removed from favorites", { name: label }),
|
|
782
|
+
intent: "accent",
|
|
783
|
+
action: { label: t("Undo"), onPress: () => toggleStar(name) },
|
|
784
|
+
}
|
|
785
|
+
: {
|
|
786
|
+
id: `${slug}-favorite-${name}`,
|
|
787
|
+
title: t("{{name}} added to favorites", { name: label }),
|
|
788
|
+
intent: "success",
|
|
789
|
+
},
|
|
790
|
+
);
|
|
791
|
+
},
|
|
792
|
+
[isFavorite, toggleStar, nameField, slug, t],
|
|
793
|
+
);
|
|
794
|
+
|
|
795
|
+
const cardFor = (doc: Partial<TDoc>): FrappeListCard => {
|
|
796
|
+
const name = doc.name ?? "";
|
|
797
|
+
const row = doc as Record<string, unknown>;
|
|
798
|
+
return {
|
|
799
|
+
href:
|
|
800
|
+
route !== undefined && name ? frappeRouteHref(route, { mode: "view", name }) : undefined,
|
|
801
|
+
open: () => onItemClick?.(doc),
|
|
802
|
+
facets: Object.fromEntries(
|
|
803
|
+
(facets ?? []).map((facet) => [
|
|
804
|
+
facet.param,
|
|
805
|
+
facet.doctype
|
|
806
|
+
? (list.facets[facet.param]?.values.get(name) ?? [])
|
|
807
|
+
: row[facet.field] === undefined || row[facet.field] === null
|
|
808
|
+
? []
|
|
809
|
+
: [String(row[facet.field])],
|
|
810
|
+
]),
|
|
811
|
+
),
|
|
812
|
+
favorite:
|
|
813
|
+
favoritesStorageKey && name
|
|
814
|
+
? { active: isFavorite(name), toggle: () => toggleFavorite(doc) }
|
|
815
|
+
: undefined,
|
|
816
|
+
};
|
|
817
|
+
};
|
|
818
|
+
const tintFacet = facets?.find((facet) => facet.tint);
|
|
819
|
+
const drawRecordCard = (doc: Partial<TDoc>, index: number) => {
|
|
820
|
+
const card = cardFor(doc);
|
|
821
|
+
const name = doc.name ?? "";
|
|
822
|
+
const values = tintFacet ? (card.facets[tintFacet.param] ?? []) : [];
|
|
823
|
+
const title = recordTitle(doc, look, nameField);
|
|
824
|
+
return (
|
|
825
|
+
<RecordCard
|
|
826
|
+
title={title}
|
|
827
|
+
subtitle={look?.subtitle?.(doc)}
|
|
828
|
+
code={look?.code?.(doc)}
|
|
829
|
+
images={look?.images?.(doc, finalBaseURL) ?? []}
|
|
830
|
+
tints={values.slice(0, 2).map((value) => tintFacet?.tint?.(value))}
|
|
831
|
+
badges={values.map((value) => ({
|
|
832
|
+
value,
|
|
833
|
+
label: tintFacet?.optionLabel?.(value) ?? value,
|
|
834
|
+
tint: tintFacet?.tint?.(value),
|
|
835
|
+
}))}
|
|
836
|
+
href={card.href}
|
|
837
|
+
onPress={card.open}
|
|
838
|
+
favorite={card.favorite}
|
|
839
|
+
lazy={index >= eagerCards}
|
|
840
|
+
testID={`${slug}-card-${name}`}
|
|
841
|
+
favoriteTestID={`${slug}-favorite-${name}`}
|
|
842
|
+
/>
|
|
843
|
+
);
|
|
844
|
+
};
|
|
845
|
+
const drawCard = renderCard
|
|
846
|
+
? (doc: Partial<TDoc>, index: number) => renderCard(doc, index, cardFor(doc))
|
|
847
|
+
: look?.images
|
|
848
|
+
? drawRecordCard
|
|
849
|
+
: undefined;
|
|
850
|
+
|
|
851
|
+
// "/" jumps to the search box on web, the common list-page affordance.
|
|
852
|
+
// Document scope adds no tab stop (LC-49); it stands down while the reader
|
|
853
|
+
// types in any editable control or works inside an open overlay.
|
|
854
|
+
useEffect(() => {
|
|
855
|
+
if (!isWeb || typeof document === "undefined" || !showSearch) return;
|
|
856
|
+
const handleKeyDown = (event: KeyboardEvent) => {
|
|
857
|
+
if (event.key !== "/" || event.metaKey || event.ctrlKey || event.altKey) return;
|
|
858
|
+
if (event.defaultPrevented) return;
|
|
859
|
+
const target = event.target as HTMLElement | null;
|
|
860
|
+
if (target) {
|
|
861
|
+
const tag = target.tagName;
|
|
862
|
+
if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return;
|
|
863
|
+
if (target.isContentEditable) return;
|
|
864
|
+
if (target.closest?.('[role="dialog"], [role="menu"], [role="listbox"]')) return;
|
|
865
|
+
}
|
|
866
|
+
const input = document.getElementById(searchId)?.querySelector("input");
|
|
867
|
+
if (!input) return;
|
|
868
|
+
event.preventDefault();
|
|
869
|
+
input.focus();
|
|
870
|
+
input.select?.();
|
|
871
|
+
};
|
|
872
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
873
|
+
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
874
|
+
}, [searchId, showSearch]);
|
|
875
|
+
|
|
876
|
+
const headerOwnsTools = route !== undefined && !showToolbar;
|
|
877
|
+
const pressFor = (action: FrappeListAction) =>
|
|
878
|
+
action.onPress ?? (action.href && navigate ? () => navigate(action.href as string) : undefined);
|
|
879
|
+
const headerActions =
|
|
880
|
+
headerOwnsTools || headerAccessory || actions?.length ? (
|
|
881
|
+
<XStack {...knobProps.gap} alignItems="center" flexWrap="wrap" flexShrink={1} minWidth={0}>
|
|
882
|
+
{headerOwnsTools ? (
|
|
883
|
+
<Tooltip content={t("Switch views")}>
|
|
884
|
+
<XStack>
|
|
885
|
+
<ViewSwitcher
|
|
886
|
+
views={[
|
|
887
|
+
...(hasCards ? [{ type: "grid", label: t("Cards"), icon: ImageIcon }] : []),
|
|
888
|
+
{ type: "list", label: t("List"), icon: ListIcon },
|
|
889
|
+
{ type: "table", label: t("Table"), icon: TableIcon },
|
|
890
|
+
]}
|
|
891
|
+
currentView={viewMode}
|
|
892
|
+
onViewChange={(v) => setViewMode(v as ListViewMode)}
|
|
893
|
+
/>
|
|
894
|
+
</XStack>
|
|
895
|
+
</Tooltip>
|
|
896
|
+
) : null}
|
|
897
|
+
{headerAccessory}
|
|
898
|
+
{actions?.map((action) => {
|
|
899
|
+
const Icon = action.icon;
|
|
900
|
+
const onPress = pressFor(action);
|
|
901
|
+
return (
|
|
902
|
+
<React.Fragment key={action.label}>
|
|
903
|
+
<Tooltip content={action.hint ?? action.label}>
|
|
904
|
+
<Button
|
|
905
|
+
outlined
|
|
906
|
+
onPress={onPress}
|
|
907
|
+
icon={Icon ? <Icon size={16} /> : undefined}
|
|
908
|
+
$max-sm={{ display: "none" }}
|
|
909
|
+
testID={action.testID}
|
|
910
|
+
>
|
|
911
|
+
{action.label}
|
|
912
|
+
</Button>
|
|
913
|
+
</Tooltip>
|
|
914
|
+
<Button
|
|
915
|
+
outlined
|
|
916
|
+
circular
|
|
917
|
+
onPress={onPress}
|
|
918
|
+
icon={Icon ? <Icon size={16} /> : undefined}
|
|
919
|
+
display="none"
|
|
920
|
+
aria-label={action.label}
|
|
921
|
+
$max-sm={{ display: "flex" }}
|
|
922
|
+
/>
|
|
923
|
+
</React.Fragment>
|
|
924
|
+
);
|
|
925
|
+
})}
|
|
926
|
+
{headerOwnsTools && onAdd ? (
|
|
927
|
+
<>
|
|
928
|
+
<Button
|
|
929
|
+
theme="accent"
|
|
930
|
+
onPress={onAdd}
|
|
931
|
+
icon={<PlusIcon size={16} />}
|
|
932
|
+
$max-sm={{ display: "none" }}
|
|
933
|
+
>
|
|
934
|
+
{t("Add {{doctype}}", { doctype })}
|
|
935
|
+
</Button>
|
|
936
|
+
<Button
|
|
937
|
+
theme="accent"
|
|
938
|
+
onPress={onAdd}
|
|
939
|
+
icon={<PlusIcon size={16} />}
|
|
940
|
+
display="none"
|
|
941
|
+
$max-sm={{ display: "flex" }}
|
|
942
|
+
>
|
|
943
|
+
{t("Add")}
|
|
944
|
+
</Button>
|
|
945
|
+
</>
|
|
946
|
+
) : null}
|
|
947
|
+
</XStack>
|
|
948
|
+
) : undefined;
|
|
949
|
+
|
|
950
|
+
const controlsBand = hasControls ? (
|
|
951
|
+
<ListControls
|
|
952
|
+
controls={listControls}
|
|
953
|
+
list={list}
|
|
954
|
+
searchId={searchId}
|
|
955
|
+
searchPlaceholder={searchPlaceholder}
|
|
956
|
+
showSearch={showSearch}
|
|
957
|
+
showFavorites={Boolean(favoritesStorageKey)}
|
|
958
|
+
shuffle={
|
|
959
|
+
shuffle
|
|
960
|
+
? {
|
|
961
|
+
open: shuffleOpen,
|
|
962
|
+
toggle: () => setShuffleOpen((open) => !open),
|
|
963
|
+
panelId: `${slug}-shuffle-panel`,
|
|
964
|
+
}
|
|
965
|
+
: undefined
|
|
966
|
+
}
|
|
967
|
+
shufflePanel={
|
|
968
|
+
shuffle && shuffleOpen && shuffleRows.length > 0 && drawCard ? (
|
|
969
|
+
<ShufflePanel<Partial<TDoc>>
|
|
970
|
+
id={`${slug}-shuffle-panel`}
|
|
971
|
+
label={t("Surprise me")}
|
|
972
|
+
rows={shuffleRows}
|
|
973
|
+
itemLabel={shuffle.label}
|
|
974
|
+
renderPick={(doc) => drawCard(doc, 0)}
|
|
975
|
+
onGo={(doc) => {
|
|
976
|
+
setShuffleOpen(false);
|
|
977
|
+
onItemClick?.(doc);
|
|
978
|
+
}}
|
|
979
|
+
testID={`${slug}-shuffle-panel`}
|
|
980
|
+
/>
|
|
981
|
+
) : null
|
|
982
|
+
}
|
|
983
|
+
separator={!surface}
|
|
984
|
+
testID={slug}
|
|
985
|
+
/>
|
|
986
|
+
) : null;
|
|
987
|
+
|
|
988
|
+
const asPage = (view: React.ReactElement) => {
|
|
989
|
+
const extras = (
|
|
990
|
+
<>
|
|
991
|
+
{list.readers}
|
|
992
|
+
{shuffle && shuffleOpen && !linkFilters.pending ? (
|
|
993
|
+
<FrappeRowsReader<TDoc>
|
|
994
|
+
connection={connectionConfig}
|
|
995
|
+
doctype={doctype}
|
|
996
|
+
options={shuffleOptions as never}
|
|
997
|
+
onChange={handleShuffleRows}
|
|
998
|
+
/>
|
|
999
|
+
) : null}
|
|
1000
|
+
{favoritesStorageKey ? <ListToastHost /> : null}
|
|
1001
|
+
</>
|
|
1002
|
+
);
|
|
1003
|
+
if (route === undefined) {
|
|
1004
|
+
return controlsBand ? (
|
|
1005
|
+
<YStack {...knobProps.gap} flex={viewMode === "grid" ? undefined : 1}>
|
|
1006
|
+
{controlsBand}
|
|
1007
|
+
{view}
|
|
1008
|
+
{extras}
|
|
1009
|
+
</YStack>
|
|
1010
|
+
) : (
|
|
1011
|
+
<>
|
|
1012
|
+
{view}
|
|
1013
|
+
{extras}
|
|
1014
|
+
</>
|
|
1015
|
+
);
|
|
1016
|
+
}
|
|
1017
|
+
return (
|
|
1018
|
+
<ListPageLayout
|
|
1019
|
+
scroll={!surface}
|
|
1020
|
+
header={
|
|
1021
|
+
<>
|
|
1022
|
+
{controlsBand ? <SkipLink targetId={resultsId}>{t("Skip to results")}</SkipLink> : null}
|
|
1023
|
+
<PageHeader
|
|
1024
|
+
title={title ?? doctype}
|
|
1025
|
+
subtitle={subtitle && list.total !== undefined ? subtitle(list.total) : undefined}
|
|
1026
|
+
actions={headerActions}
|
|
1027
|
+
divider={!surface}
|
|
1028
|
+
/>
|
|
1029
|
+
</>
|
|
1030
|
+
}
|
|
1031
|
+
toolbar={controlsBand ?? undefined}
|
|
1032
|
+
>
|
|
1033
|
+
{view}
|
|
1034
|
+
{extras}
|
|
1035
|
+
</ListPageLayout>
|
|
1036
|
+
);
|
|
1037
|
+
};
|
|
1038
|
+
|
|
1039
|
+
const controlsEmptyState = hasControls ? (
|
|
1040
|
+
<EmptyState
|
|
1041
|
+
aria-live="polite"
|
|
1042
|
+
icon={
|
|
1043
|
+
list.state.favoritesOnly ? (
|
|
1044
|
+
<HeartIcon size={28} />
|
|
1045
|
+
) : isListNarrowed(list.state) ? (
|
|
1046
|
+
<MagnifyingGlassIcon size={28} />
|
|
1047
|
+
) : (
|
|
1048
|
+
<SparkleIcon size={28} />
|
|
1049
|
+
)
|
|
1050
|
+
}
|
|
1051
|
+
title={
|
|
1052
|
+
list.state.favoritesOnly
|
|
1053
|
+
? t("No favorites yet")
|
|
1054
|
+
: list.state.search.trim()
|
|
1055
|
+
? t('No match for "{{query}}"', { query: list.state.search.trim() })
|
|
1056
|
+
: isListNarrowed(list.state)
|
|
1057
|
+
? t("No {{doctype}} match the active filters", { doctype })
|
|
1058
|
+
: t("No {{doctype}} yet", { doctype })
|
|
1059
|
+
}
|
|
1060
|
+
description={
|
|
1061
|
+
list.state.favoritesOnly
|
|
1062
|
+
? t("Tap the heart on a record to favorite it.")
|
|
1063
|
+
: isListNarrowed(list.state)
|
|
1064
|
+
? t("Try a different search or filter.")
|
|
1065
|
+
: undefined
|
|
1066
|
+
}
|
|
1067
|
+
action={
|
|
1068
|
+
isListNarrowed(list.state) ? (
|
|
1069
|
+
<Button outlined onPress={list.clear}>
|
|
1070
|
+
{t("Clear filters")}
|
|
1071
|
+
</Button>
|
|
1072
|
+
) : onAdd ? (
|
|
1073
|
+
<Button theme="accent" onPress={onAdd}>
|
|
1074
|
+
{t("Add your first {{doctype}}", { doctype })}
|
|
1075
|
+
</Button>
|
|
1076
|
+
) : undefined
|
|
1077
|
+
}
|
|
1078
|
+
/>
|
|
1079
|
+
) : undefined;
|
|
1080
|
+
|
|
554
1081
|
// Named fields need no schema to read or draw: the view renders now (on
|
|
555
1082
|
// the server too) and labels settle when meta lands.
|
|
556
1083
|
if (isMetaLoading && !fields) {
|
|
@@ -559,10 +1086,11 @@ function FrappeListViewBody<TDoc extends { name: string; doctype: string }>({
|
|
|
559
1086
|
{...knobProps.panelPadding}
|
|
560
1087
|
alignItems="center"
|
|
561
1088
|
justifyContent="center"
|
|
1089
|
+
gap="$2"
|
|
562
1090
|
{...stackProps}
|
|
563
1091
|
>
|
|
564
1092
|
<Spinner size="large" />
|
|
565
|
-
<Text {...knobProps.body} color={knobProps.textAccentColor}
|
|
1093
|
+
<Text {...knobProps.body} color={knobProps.textAccentColor}>
|
|
566
1094
|
{t("Loading {{doctype}} schema...", { doctype })}
|
|
567
1095
|
</Text>
|
|
568
1096
|
</YStack>,
|
|
@@ -586,7 +1114,7 @@ function FrappeListViewBody<TDoc extends { name: string; doctype: string }>({
|
|
|
586
1114
|
<XStack alignItems="center" {...knobProps.gap}>
|
|
587
1115
|
<ViewSwitcher
|
|
588
1116
|
views={[
|
|
589
|
-
...(
|
|
1117
|
+
...(hasCards ? [{ type: "grid", label: t("Cards") }] : []),
|
|
590
1118
|
{ type: "list", label: t("List") },
|
|
591
1119
|
{ type: "table", label: t("Table") },
|
|
592
1120
|
]}
|
|
@@ -691,7 +1219,7 @@ function FrappeListViewBody<TDoc extends { name: string; doctype: string }>({
|
|
|
691
1219
|
borderBottomWidth={1}
|
|
692
1220
|
borderBottomColor="$borderColor"
|
|
693
1221
|
>
|
|
694
|
-
<View flex={1}>
|
|
1222
|
+
<View flex={1} id={searchId}>
|
|
695
1223
|
<SearchInput
|
|
696
1224
|
value={searchText}
|
|
697
1225
|
onChange={setSearchText}
|
|
@@ -711,19 +1239,19 @@ function FrappeListViewBody<TDoc extends { name: string; doctype: string }>({
|
|
|
711
1239
|
const isGrid = viewMode === "grid";
|
|
712
1240
|
return (
|
|
713
1241
|
<YStack flex={isGrid ? undefined : 1}>
|
|
714
|
-
{showSearch ? renderListSearch() : null}
|
|
1242
|
+
{showSearch && !hasControls ? renderListSearch() : null}
|
|
715
1243
|
<FrappeList
|
|
716
1244
|
key={`${viewMode}-${refreshKey}`}
|
|
717
1245
|
layout={isGrid ? "grid" : "list"}
|
|
718
1246
|
gridProps={gridProps}
|
|
719
|
-
emptyState={emptyState}
|
|
1247
|
+
emptyState={emptyState ?? controlsEmptyState}
|
|
720
1248
|
pageSize={pageSize}
|
|
721
1249
|
onRetry={onRetry ?? handleRefresh}
|
|
722
1250
|
doctype={doctype}
|
|
723
1251
|
fields={effectiveFields as readonly ("name" | "doctype")[]}
|
|
724
1252
|
filters={listFilters as Partial<{ name: string; doctype: string }>}
|
|
725
1253
|
renderItem={
|
|
726
|
-
(isGrid &&
|
|
1254
|
+
(isGrid && drawCard ? drawCard : renderListItem) as unknown as (
|
|
727
1255
|
item: Partial<{ name: string; doctype: string }>,
|
|
728
1256
|
index: number,
|
|
729
1257
|
) => React.ReactNode
|
|
@@ -736,10 +1264,12 @@ function FrappeListViewBody<TDoc extends { name: string; doctype: string }>({
|
|
|
736
1264
|
baseURL={finalBaseURL}
|
|
737
1265
|
auth={finalAuth}
|
|
738
1266
|
syncOptions={finalSyncOptions}
|
|
739
|
-
orderBy={
|
|
1267
|
+
orderBy={
|
|
1268
|
+
effectiveOrderBy as FrappeListProps<{ name: string; doctype: string }>["orderBy"]
|
|
1269
|
+
}
|
|
740
1270
|
limit={limit}
|
|
741
1271
|
initialRows={
|
|
742
|
-
|
|
1272
|
+
seedRows as FrappeListProps<{ name: string; doctype: string }>["initialRows"]
|
|
743
1273
|
}
|
|
744
1274
|
// Only override when narrowing is active: FrappeList's own
|
|
745
1275
|
// "No {doctype} records found" stays the true-empty message.
|
|
@@ -792,6 +1322,7 @@ function FrappeListViewBody<TDoc extends { name: string; doctype: string }>({
|
|
|
792
1322
|
{...(surface ? knobProps.containerRadius : {})}
|
|
793
1323
|
overflow={surface ? "hidden" : undefined}
|
|
794
1324
|
testID={`${doctype.toLowerCase()}-list`}
|
|
1325
|
+
{...(hasControls ? { id: resultsId } : {})}
|
|
795
1326
|
{...stackProps}
|
|
796
1327
|
>
|
|
797
1328
|
{renderToolbar()}
|