@multiplatform.one/frappe-ui 7.66.0 → 7.67.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/FormSidebar.cjs +95 -194
- package/dist/cjs/FormSidebar.native.js +107 -217
- package/dist/cjs/FormSidebar.native.js.map +1 -1
- package/dist/cjs/FrappeConnectionIndicator.cjs +5 -24
- package/dist/cjs/FrappeConnectionIndicator.native.js +5 -24
- package/dist/cjs/FrappeConnectionIndicator.native.js.map +1 -1
- package/dist/cjs/FrappeImageView.cjs +46 -65
- package/dist/cjs/FrappeImageView.native.js +46 -68
- package/dist/cjs/FrappeImageView.native.js.map +1 -1
- package/dist/cjs/FrappeListView.cjs +24 -67
- package/dist/cjs/FrappeListView.native.js +25 -71
- package/dist/cjs/FrappeListView.native.js.map +1 -1
- package/dist/cjs/FrappeRecordView.cjs +4 -0
- package/dist/cjs/FrappeRecordView.native.js +4 -0
- package/dist/cjs/FrappeRecordView.native.js.map +1 -1
- package/dist/cjs/FrappeReportBuilder.cjs +21 -18
- package/dist/cjs/FrappeReportBuilder.native.js +21 -18
- package/dist/cjs/FrappeReportBuilder.native.js.map +1 -1
- package/dist/cjs/SyncErrorNotice.cjs +8 -36
- package/dist/cjs/SyncErrorNotice.native.js +8 -36
- package/dist/cjs/SyncErrorNotice.native.js.map +1 -1
- package/dist/cjs/desk/DeskFormSidebar.cjs +1 -6
- package/dist/cjs/desk/DeskFormSidebar.native.js +1 -6
- package/dist/cjs/desk/DeskFormSidebar.native.js.map +1 -1
- package/dist/cjs/desk/DeskShell.native.js.map +1 -1
- package/dist/cjs/fields/Date/catalog.native.js +0 -1
- package/dist/cjs/fields/Date/catalog.native.js.map +1 -1
- package/dist/cjs/fields/Date/index.native.js +4 -363
- package/dist/cjs/fields/Date/index.native.js.map +1 -1
- package/dist/cjs/insights/InsightsFacetTags.cjs +16 -92
- package/dist/cjs/insights/InsightsFacetTags.native.js +15 -97
- package/dist/cjs/insights/InsightsFacetTags.native.js.map +1 -1
- package/dist/cjs/list/FacetBadge.cjs +4 -28
- package/dist/cjs/list/FacetBadge.native.js +4 -28
- package/dist/cjs/list/FacetBadge.native.js.map +1 -1
- package/dist/cjs/list/FacetRail.cjs +37 -128
- package/dist/cjs/list/FacetRail.native.js +32 -130
- package/dist/cjs/list/FacetRail.native.js.map +1 -1
- package/dist/cjs/list/RecordCard.cjs +107 -127
- package/dist/cjs/list/RecordCard.native.js +86 -96
- package/dist/cjs/list/RecordCard.native.js.map +1 -1
- package/dist/cjs/list/ShufflePanel.cjs +57 -36
- package/dist/cjs/list/ShufflePanel.native.js +64 -39
- package/dist/cjs/list/ShufflePanel.native.js.map +1 -1
- package/dist/cjs/record/RecordHero.cjs +7 -85
- package/dist/cjs/record/RecordHero.native.js +7 -85
- package/dist/cjs/record/RecordHero.native.js.map +1 -1
- package/dist/cjs/record/RecordPage.cjs +1 -0
- package/dist/cjs/record/RecordPage.native.js +1 -0
- package/dist/cjs/record/RecordPage.native.js.map +1 -1
- package/dist/cjs/report/ReportViewToolbar.cjs +70 -39
- package/dist/cjs/report/ReportViewToolbar.native.js +73 -40
- package/dist/cjs/report/ReportViewToolbar.native.js.map +1 -1
- package/dist/esm/FormSidebar.mjs +98 -197
- package/dist/esm/FormSidebar.mjs.map +1 -1
- package/dist/esm/FormSidebar.native.js +110 -220
- package/dist/esm/FormSidebar.native.js.map +1 -1
- package/dist/esm/FrappeConnectionIndicator.mjs +7 -26
- package/dist/esm/FrappeConnectionIndicator.mjs.map +1 -1
- package/dist/esm/FrappeConnectionIndicator.native.js +7 -26
- package/dist/esm/FrappeConnectionIndicator.native.js.map +1 -1
- package/dist/esm/FrappeImageView.mjs +48 -67
- package/dist/esm/FrappeImageView.mjs.map +1 -1
- package/dist/esm/FrappeImageView.native.js +48 -70
- package/dist/esm/FrappeImageView.native.js.map +1 -1
- package/dist/esm/FrappeListView.mjs +25 -68
- package/dist/esm/FrappeListView.mjs.map +1 -1
- package/dist/esm/FrappeListView.native.js +26 -72
- package/dist/esm/FrappeListView.native.js.map +1 -1
- package/dist/esm/FrappeRecordView.mjs +4 -0
- package/dist/esm/FrappeRecordView.mjs.map +1 -1
- package/dist/esm/FrappeRecordView.native.js +4 -0
- package/dist/esm/FrappeRecordView.native.js.map +1 -1
- package/dist/esm/FrappeReportBuilder.mjs +21 -18
- package/dist/esm/FrappeReportBuilder.mjs.map +1 -1
- package/dist/esm/FrappeReportBuilder.native.js +21 -18
- package/dist/esm/FrappeReportBuilder.native.js.map +1 -1
- package/dist/esm/SyncErrorNotice.mjs +10 -38
- package/dist/esm/SyncErrorNotice.mjs.map +1 -1
- package/dist/esm/SyncErrorNotice.native.js +10 -38
- package/dist/esm/SyncErrorNotice.native.js.map +1 -1
- package/dist/esm/desk/DeskFormSidebar.mjs +1 -6
- package/dist/esm/desk/DeskFormSidebar.mjs.map +1 -1
- package/dist/esm/desk/DeskFormSidebar.native.js +1 -6
- package/dist/esm/desk/DeskFormSidebar.native.js.map +1 -1
- package/dist/esm/desk/DeskShell.mjs.map +1 -1
- package/dist/esm/desk/DeskShell.native.js.map +1 -1
- package/dist/esm/fields/Date/catalog.mjs.map +1 -1
- package/dist/esm/fields/Date/catalog.native.js +0 -1
- package/dist/esm/fields/Date/catalog.native.js.map +1 -1
- package/dist/esm/fields/Date/index.native.js +2 -361
- package/dist/esm/fields/Date/index.native.js.map +1 -1
- package/dist/esm/insights/InsightsFacetTags.mjs +18 -94
- package/dist/esm/insights/InsightsFacetTags.mjs.map +1 -1
- package/dist/esm/insights/InsightsFacetTags.native.js +17 -99
- package/dist/esm/insights/InsightsFacetTags.native.js.map +1 -1
- package/dist/esm/list/FacetBadge.mjs +5 -29
- package/dist/esm/list/FacetBadge.mjs.map +1 -1
- package/dist/esm/list/FacetBadge.native.js +5 -29
- package/dist/esm/list/FacetBadge.native.js.map +1 -1
- package/dist/esm/list/FacetRail.mjs +40 -131
- package/dist/esm/list/FacetRail.mjs.map +1 -1
- package/dist/esm/list/FacetRail.native.js +35 -133
- package/dist/esm/list/FacetRail.native.js.map +1 -1
- package/dist/esm/list/RecordCard.mjs +107 -127
- package/dist/esm/list/RecordCard.mjs.map +1 -1
- package/dist/esm/list/RecordCard.native.js +86 -96
- package/dist/esm/list/RecordCard.native.js.map +1 -1
- package/dist/esm/list/ShufflePanel.mjs +59 -38
- package/dist/esm/list/ShufflePanel.mjs.map +1 -1
- package/dist/esm/list/ShufflePanel.native.js +66 -41
- package/dist/esm/list/ShufflePanel.native.js.map +1 -1
- package/dist/esm/record/RecordHero.mjs +8 -86
- package/dist/esm/record/RecordHero.mjs.map +1 -1
- package/dist/esm/record/RecordHero.native.js +8 -86
- package/dist/esm/record/RecordHero.native.js.map +1 -1
- package/dist/esm/record/RecordPage.mjs +2 -1
- package/dist/esm/record/RecordPage.mjs.map +1 -1
- package/dist/esm/record/RecordPage.native.js +2 -1
- package/dist/esm/record/RecordPage.native.js.map +1 -1
- package/dist/esm/report/ReportViewToolbar.mjs +72 -41
- package/dist/esm/report/ReportViewToolbar.mjs.map +1 -1
- package/dist/esm/report/ReportViewToolbar.native.js +75 -42
- package/dist/esm/report/ReportViewToolbar.native.js.map +1 -1
- package/dist/jsx/FormSidebar.mjs +98 -197
- package/dist/jsx/FormSidebar.mjs.map +1 -1
- package/dist/jsx/FormSidebar.native.js +107 -217
- package/dist/jsx/FormSidebar.native.js.map +1 -1
- package/dist/jsx/FrappeConnectionIndicator.mjs +7 -26
- package/dist/jsx/FrappeConnectionIndicator.mjs.map +1 -1
- package/dist/jsx/FrappeConnectionIndicator.native.js +5 -24
- package/dist/jsx/FrappeConnectionIndicator.native.js.map +1 -1
- package/dist/jsx/FrappeImageView.mjs +48 -67
- package/dist/jsx/FrappeImageView.mjs.map +1 -1
- package/dist/jsx/FrappeImageView.native.js +46 -68
- package/dist/jsx/FrappeImageView.native.js.map +1 -1
- package/dist/jsx/FrappeListView.mjs +25 -68
- package/dist/jsx/FrappeListView.mjs.map +1 -1
- package/dist/jsx/FrappeListView.native.js +25 -71
- package/dist/jsx/FrappeListView.native.js.map +1 -1
- package/dist/jsx/FrappeRecordView.mjs +4 -0
- package/dist/jsx/FrappeRecordView.mjs.map +1 -1
- package/dist/jsx/FrappeRecordView.native.js +4 -0
- package/dist/jsx/FrappeRecordView.native.js.map +1 -1
- package/dist/jsx/FrappeReportBuilder.mjs +21 -18
- package/dist/jsx/FrappeReportBuilder.mjs.map +1 -1
- package/dist/jsx/FrappeReportBuilder.native.js +21 -18
- package/dist/jsx/FrappeReportBuilder.native.js.map +1 -1
- package/dist/jsx/SyncErrorNotice.mjs +10 -38
- package/dist/jsx/SyncErrorNotice.mjs.map +1 -1
- package/dist/jsx/SyncErrorNotice.native.js +8 -36
- package/dist/jsx/SyncErrorNotice.native.js.map +1 -1
- package/dist/jsx/desk/DeskFormSidebar.mjs +1 -6
- package/dist/jsx/desk/DeskFormSidebar.mjs.map +1 -1
- package/dist/jsx/desk/DeskFormSidebar.native.js +1 -6
- package/dist/jsx/desk/DeskFormSidebar.native.js.map +1 -1
- package/dist/jsx/desk/DeskShell.mjs.map +1 -1
- package/dist/jsx/desk/DeskShell.native.js.map +1 -1
- package/dist/jsx/fields/Date/catalog.mjs.map +1 -1
- package/dist/jsx/fields/Date/catalog.native.js +0 -1
- package/dist/jsx/fields/Date/catalog.native.js.map +1 -1
- package/dist/jsx/fields/Date/index.native.js +4 -363
- package/dist/jsx/fields/Date/index.native.js.map +1 -1
- package/dist/jsx/insights/InsightsFacetTags.mjs +18 -94
- package/dist/jsx/insights/InsightsFacetTags.mjs.map +1 -1
- package/dist/jsx/insights/InsightsFacetTags.native.js +15 -97
- package/dist/jsx/insights/InsightsFacetTags.native.js.map +1 -1
- package/dist/jsx/list/FacetBadge.mjs +5 -29
- package/dist/jsx/list/FacetBadge.mjs.map +1 -1
- package/dist/jsx/list/FacetBadge.native.js +4 -28
- package/dist/jsx/list/FacetBadge.native.js.map +1 -1
- package/dist/jsx/list/FacetRail.mjs +40 -131
- package/dist/jsx/list/FacetRail.mjs.map +1 -1
- package/dist/jsx/list/FacetRail.native.js +32 -130
- package/dist/jsx/list/FacetRail.native.js.map +1 -1
- package/dist/jsx/list/RecordCard.mjs +107 -127
- package/dist/jsx/list/RecordCard.mjs.map +1 -1
- package/dist/jsx/list/RecordCard.native.js +86 -96
- package/dist/jsx/list/RecordCard.native.js.map +1 -1
- package/dist/jsx/list/ShufflePanel.mjs +59 -38
- package/dist/jsx/list/ShufflePanel.mjs.map +1 -1
- package/dist/jsx/list/ShufflePanel.native.js +64 -39
- package/dist/jsx/list/ShufflePanel.native.js.map +1 -1
- package/dist/jsx/record/RecordHero.mjs +8 -86
- package/dist/jsx/record/RecordHero.mjs.map +1 -1
- package/dist/jsx/record/RecordHero.native.js +7 -85
- package/dist/jsx/record/RecordHero.native.js.map +1 -1
- package/dist/jsx/record/RecordPage.mjs +2 -1
- package/dist/jsx/record/RecordPage.mjs.map +1 -1
- package/dist/jsx/record/RecordPage.native.js +1 -0
- package/dist/jsx/record/RecordPage.native.js.map +1 -1
- package/dist/jsx/report/ReportViewToolbar.mjs +72 -41
- package/dist/jsx/report/ReportViewToolbar.mjs.map +1 -1
- package/dist/jsx/report/ReportViewToolbar.native.js +73 -40
- package/dist/jsx/report/ReportViewToolbar.native.js.map +1 -1
- package/package.json +16 -16
- package/src/FormSidebar.stories.tsx +1 -1
- package/src/FormSidebar.tsx +128 -253
- package/src/FrappeConnectionIndicator.tsx +6 -28
- package/src/FrappeImageView.stories.tsx +1 -6
- package/src/FrappeImageView.tsx +52 -66
- package/src/FrappeListView.controls.spec.tsx +3 -2
- package/src/FrappeListView.tsx +23 -55
- package/src/FrappeRecordView.tsx +16 -3
- package/src/FrappeReportBuilder.tsx +21 -19
- package/src/SyncErrorNotice.spec.tsx +7 -12
- package/src/SyncErrorNotice.stories.tsx +2 -4
- package/src/SyncErrorNotice.tsx +13 -41
- package/src/desk/DeskFormSidebar.tsx +1 -2
- package/src/desk/DeskShell.tsx +1 -0
- package/src/fields/Date/catalog.tsx +6 -7
- package/src/fields/Date/nativeIsCatalog.spec.tsx +26 -0
- package/src/fields/Date/typeParity.spec.tsx +20 -98
- package/src/frappeRemainder.designLaw.spec.ts +8 -7
- package/src/insights/InsightsFacetTags.tsx +21 -102
- package/src/list/FacetBadge.tsx +7 -29
- package/src/list/FacetRail.spec.tsx +11 -10
- package/src/list/FacetRail.tsx +49 -161
- package/src/list/RecordCard.native.tsx +63 -64
- package/src/list/RecordCard.spec.tsx +8 -5
- package/src/list/RecordCard.tsx +116 -129
- package/src/list/ShufflePanel.tsx +65 -38
- package/src/record/RecordHero.tsx +11 -51
- package/src/record/RecordPage.tsx +6 -1
- package/src/report/ReportViewToolbar.spec.tsx +45 -3
- package/src/report/ReportViewToolbar.tsx +109 -56
- package/types/FormSidebar.d.ts +13 -3
- package/types/FormSidebar.d.ts.map +1 -1
- package/types/FrappeConnectionIndicator.d.ts +2 -1
- package/types/FrappeConnectionIndicator.d.ts.map +1 -1
- package/types/FrappeImageView.d.ts.map +1 -1
- package/types/FrappeListView.d.ts.map +1 -1
- package/types/FrappeRecordView.d.ts.map +1 -1
- package/types/FrappeReportBuilder.d.ts.map +1 -1
- package/types/SyncErrorNotice.d.ts +4 -6
- package/types/SyncErrorNotice.d.ts.map +1 -1
- package/types/desk/DeskFormSidebar.d.ts.map +1 -1
- package/types/desk/DeskShell.d.ts.map +1 -1
- package/types/fields/Date/catalog.d.ts +5 -4
- package/types/fields/Date/catalog.d.ts.map +1 -1
- package/types/insights/InsightsFacetTags.d.ts +4 -1
- package/types/insights/InsightsFacetTags.d.ts.map +1 -1
- package/types/list/FacetBadge.d.ts +3 -3
- package/types/list/FacetBadge.d.ts.map +1 -1
- package/types/list/FacetRail.d.ts +7 -5
- package/types/list/FacetRail.d.ts.map +1 -1
- package/types/list/RecordCard.d.ts +6 -6
- package/types/list/RecordCard.d.ts.map +1 -1
- package/types/list/RecordCard.native.d.ts +4 -3
- package/types/list/RecordCard.native.d.ts.map +1 -1
- package/types/list/ShufflePanel.d.ts +8 -3
- package/types/list/ShufflePanel.d.ts.map +1 -1
- package/types/record/RecordHero.d.ts +5 -1
- package/types/record/RecordHero.d.ts.map +1 -1
- package/types/record/RecordPage.d.ts.map +1 -1
- package/types/report/ReportViewToolbar.d.ts.map +1 -1
- package/dist/cjs/fields/Date/dateTimePickerLoader.cjs +0 -38
- package/dist/cjs/fields/Date/dateTimePickerLoader.native.js +0 -42
- package/dist/cjs/fields/Date/dateTimePickerLoader.native.js.map +0 -1
- package/dist/cjs/fields/Date/sheetKeyboard.cjs +0 -54
- package/dist/cjs/fields/Date/sheetKeyboard.native.js +0 -57
- package/dist/cjs/fields/Date/sheetKeyboard.native.js.map +0 -1
- package/dist/esm/fields/Date/dateTimePickerLoader.mjs +0 -13
- package/dist/esm/fields/Date/dateTimePickerLoader.mjs.map +0 -1
- package/dist/esm/fields/Date/dateTimePickerLoader.native.js +0 -14
- package/dist/esm/fields/Date/dateTimePickerLoader.native.js.map +0 -1
- package/dist/esm/fields/Date/sheetKeyboard.mjs +0 -21
- package/dist/esm/fields/Date/sheetKeyboard.mjs.map +0 -1
- package/dist/esm/fields/Date/sheetKeyboard.native.js +0 -21
- package/dist/esm/fields/Date/sheetKeyboard.native.js.map +0 -1
- package/dist/jsx/fields/Date/dateTimePickerLoader.mjs +0 -13
- package/dist/jsx/fields/Date/dateTimePickerLoader.mjs.map +0 -1
- package/dist/jsx/fields/Date/dateTimePickerLoader.native.js +0 -42
- package/dist/jsx/fields/Date/dateTimePickerLoader.native.js.map +0 -1
- package/dist/jsx/fields/Date/sheetKeyboard.mjs +0 -21
- package/dist/jsx/fields/Date/sheetKeyboard.mjs.map +0 -1
- package/dist/jsx/fields/Date/sheetKeyboard.native.js +0 -57
- package/dist/jsx/fields/Date/sheetKeyboard.native.js.map +0 -1
- package/src/fields/Date/dateTimePickerLoader.ts +0 -25
- package/src/fields/Date/index.native.tsx +0 -445
- package/src/fields/Date/nativeFallback.spec.tsx +0 -21
- package/src/fields/Date/nativePresentation.spec.tsx +0 -278
- package/src/fields/Date/sheetKeyboard.spec.ts +0 -60
- package/src/fields/Date/sheetKeyboard.ts +0 -45
- package/types/fields/Date/dateTimePickerLoader.d.ts +0 -11
- package/types/fields/Date/dateTimePickerLoader.d.ts.map +0 -1
- package/types/fields/Date/index.native.d.ts +0 -14
- package/types/fields/Date/index.native.d.ts.map +0 -1
- package/types/fields/Date/sheetKeyboard.d.ts +0 -20
- package/types/fields/Date/sheetKeyboard.d.ts.map +0 -1
package/src/FrappeImageView.tsx
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { useDocTypeMeta } from "@multiplatform.one/frappe";
|
|
2
|
-
import { AsyncSkeleton,
|
|
2
|
+
import { AsyncSkeleton, Image, ImageGrid } from "@multiplatform.one/components";
|
|
3
3
|
import { ImageSquareIcon } from "@phosphor-icons/react";
|
|
4
4
|
import React, { useMemo } from "react";
|
|
5
5
|
import { useTranslation } from "react-i18next";
|
|
6
6
|
import { Spinner, formCommonColors } from "@multiplatform.one/forms";
|
|
7
|
-
import {
|
|
7
|
+
import { Text, YStack, useTheme, type YStackProps } from "tamagui";
|
|
8
8
|
import { isServer } from "@multiplatform.one/platform";
|
|
9
9
|
import { useResolvedKnobs } from "@multiplatform.one/theme";
|
|
10
10
|
import { useFrappeConfig } from "./FrappeProvider";
|
|
@@ -105,6 +105,7 @@ export function FrappeImageView<TDoc extends { name: string; doctype: string }>(
|
|
|
105
105
|
if (ssrItems && ssrItems.length > 0 && imageField && baseURL) {
|
|
106
106
|
return (
|
|
107
107
|
<ImageGrid
|
|
108
|
+
variant="photos"
|
|
108
109
|
items={ssrItems}
|
|
109
110
|
columns={columns}
|
|
110
111
|
onItemClick={onItemClick}
|
|
@@ -112,14 +113,13 @@ export function FrappeImageView<TDoc extends { name: string; doctype: string }>(
|
|
|
112
113
|
testID={`${doctype.toLowerCase()}-list`}
|
|
113
114
|
{...(stackProps as any)}
|
|
114
115
|
renderItem={(doc) => (
|
|
115
|
-
<
|
|
116
|
+
<FrappePhoto
|
|
116
117
|
doc={doc as Record<string, unknown>}
|
|
117
118
|
doctype={doctype}
|
|
118
119
|
imageField={imageField}
|
|
119
120
|
titleField={titleField}
|
|
120
121
|
subtitleField={subtitleField}
|
|
121
122
|
baseURL={baseURL}
|
|
122
|
-
onItemClick={onItemClick as ((doc: Partial<unknown>) => void) | undefined}
|
|
123
123
|
knobProps={knobProps}
|
|
124
124
|
/>
|
|
125
125
|
)}
|
|
@@ -283,6 +283,7 @@ function FrappeImageViewClient<TDoc extends { name: string; doctype: string }>({
|
|
|
283
283
|
<>
|
|
284
284
|
<FrappeReconnectIndicator config={appConfig} />
|
|
285
285
|
<ImageGrid
|
|
286
|
+
variant="photos"
|
|
286
287
|
items={items}
|
|
287
288
|
columns={columns}
|
|
288
289
|
onItemClick={onItemClick}
|
|
@@ -290,14 +291,13 @@ function FrappeImageViewClient<TDoc extends { name: string; doctype: string }>({
|
|
|
290
291
|
testID={`${doctype.toLowerCase()}-list`}
|
|
291
292
|
{...(stackProps as any)}
|
|
292
293
|
renderItem={(doc) => (
|
|
293
|
-
<
|
|
294
|
+
<FrappePhoto
|
|
294
295
|
doc={doc as Record<string, unknown>}
|
|
295
296
|
doctype={doctype}
|
|
296
297
|
imageField={effectiveImageField}
|
|
297
298
|
titleField={effectiveTitleField}
|
|
298
299
|
subtitleField={subtitleField}
|
|
299
300
|
baseURL={finalBaseURL}
|
|
300
|
-
onItemClick={onItemClick as ((doc: Partial<unknown>) => void) | undefined}
|
|
301
301
|
knobProps={knobProps}
|
|
302
302
|
/>
|
|
303
303
|
)}
|
|
@@ -307,18 +307,18 @@ function FrappeImageViewClient<TDoc extends { name: string; doctype: string }>({
|
|
|
307
307
|
}
|
|
308
308
|
|
|
309
309
|
/**
|
|
310
|
-
*
|
|
311
|
-
* matching markup
|
|
312
|
-
*
|
|
310
|
+
* One photo in the Photos grid (LC-264), shared between SSR and client paths so they
|
|
311
|
+
* produce matching markup: the image fills a square edge to edge with no card
|
|
312
|
+
* chrome. The title is its accessible name; it shows as a caption only when
|
|
313
|
+
* there is no picture to look at (no URL, or the image failed to load).
|
|
313
314
|
*/
|
|
314
|
-
function
|
|
315
|
+
function FrappePhoto({
|
|
315
316
|
doc,
|
|
316
317
|
doctype,
|
|
317
318
|
imageField,
|
|
318
319
|
titleField,
|
|
319
320
|
subtitleField,
|
|
320
321
|
baseURL,
|
|
321
|
-
onItemClick,
|
|
322
322
|
knobProps,
|
|
323
323
|
}: {
|
|
324
324
|
doc: Record<string, unknown>;
|
|
@@ -327,74 +327,60 @@ function FrappeImageCard({
|
|
|
327
327
|
titleField: string;
|
|
328
328
|
subtitleField?: string;
|
|
329
329
|
baseURL: string;
|
|
330
|
-
onItemClick?: (doc: Partial<unknown>) => void;
|
|
331
330
|
knobProps: ReturnType<typeof useResolvedKnobs>["knobProps"];
|
|
332
331
|
}) {
|
|
333
|
-
const theme = useTheme();
|
|
334
332
|
const imageUrl = getImageUrl(doc[imageField], baseURL);
|
|
335
|
-
const title = doc[titleField] || doc.name;
|
|
333
|
+
const title = String(doc[titleField] || doc.name);
|
|
336
334
|
const subtitle = subtitleField ? doc[subtitleField] : undefined;
|
|
337
|
-
const
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
onPress={(e) => {
|
|
348
|
-
// Bind click directly to `doc` so the correct item is used even
|
|
349
|
-
// if the ImageGrid wrapper's closure captured a stale/recycled item.
|
|
350
|
-
(e as unknown as { stopPropagation?: () => void })?.stopPropagation?.();
|
|
351
|
-
onItemClick?.(doc as Partial<unknown>);
|
|
352
|
-
}}
|
|
335
|
+
const slug = doctype.toLowerCase();
|
|
336
|
+
const caption = (
|
|
337
|
+
<YStack
|
|
338
|
+
width="100%"
|
|
339
|
+
aspectRatio={1}
|
|
340
|
+
alignItems="center"
|
|
341
|
+
justifyContent="center"
|
|
342
|
+
backgroundColor="$color3"
|
|
343
|
+
padding="$2"
|
|
344
|
+
gap="$1"
|
|
353
345
|
>
|
|
346
|
+
<Text
|
|
347
|
+
testID={`${slug}-name-${doc.name}`}
|
|
348
|
+
{...knobProps.label}
|
|
349
|
+
color={knobProps.textAccentColor}
|
|
350
|
+
textAlign="center"
|
|
351
|
+
numberOfLines={2}
|
|
352
|
+
>
|
|
353
|
+
{title}
|
|
354
|
+
</Text>
|
|
355
|
+
{subtitle != null ? (
|
|
356
|
+
<Text
|
|
357
|
+
testID={`${slug}-subtitle-${doc.name}`}
|
|
358
|
+
{...knobProps.label}
|
|
359
|
+
color={formCommonColors.muted}
|
|
360
|
+
textAlign="center"
|
|
361
|
+
numberOfLines={1}
|
|
362
|
+
>
|
|
363
|
+
{String(subtitle)}
|
|
364
|
+
</Text>
|
|
365
|
+
) : null}
|
|
366
|
+
</YStack>
|
|
367
|
+
);
|
|
368
|
+
return (
|
|
369
|
+
<YStack testID={`${slug}-card-${doc.name}`} aria-label={title} width="100%">
|
|
354
370
|
{imageUrl ? (
|
|
355
371
|
<Image
|
|
356
372
|
src={imageUrl}
|
|
357
|
-
alt={
|
|
373
|
+
alt={title}
|
|
358
374
|
width="100%"
|
|
359
375
|
aspectRatio={1}
|
|
360
376
|
objectFit="cover"
|
|
361
|
-
|
|
362
|
-
|
|
377
|
+
borderRadius={0}
|
|
378
|
+
backgroundColor="$color3"
|
|
379
|
+
fallback={caption}
|
|
363
380
|
/>
|
|
364
381
|
) : (
|
|
365
|
-
|
|
366
|
-
width="100%"
|
|
367
|
-
aspectRatio={1}
|
|
368
|
-
backgroundColor="$color2"
|
|
369
|
-
{...knobProps.borderRadius}
|
|
370
|
-
alignItems="center"
|
|
371
|
-
justifyContent="center"
|
|
372
|
-
>
|
|
373
|
-
<ImageSquareIcon size={iconSize * 2} color={theme.color11?.val ?? "currentColor"} />
|
|
374
|
-
</YStack>
|
|
382
|
+
caption
|
|
375
383
|
)}
|
|
376
|
-
|
|
377
|
-
<YStack {...knobProps.gap}>
|
|
378
|
-
<Text
|
|
379
|
-
testID={`${doctype.toLowerCase()}-name-${doc.name}`}
|
|
380
|
-
{...knobProps.heading}
|
|
381
|
-
fontWeight="400"
|
|
382
|
-
numberOfLines={1}
|
|
383
|
-
>
|
|
384
|
-
{String(title)}
|
|
385
|
-
</Text>
|
|
386
|
-
{subtitle != null ? (
|
|
387
|
-
<Text
|
|
388
|
-
testID={`${doctype.toLowerCase()}-subtitle-${doc.name}`}
|
|
389
|
-
color={formCommonColors.muted}
|
|
390
|
-
{...knobProps.label}
|
|
391
|
-
numberOfLines={1}
|
|
392
|
-
>
|
|
393
|
-
{String(subtitle)}
|
|
394
|
-
</Text>
|
|
395
|
-
) : null}
|
|
396
|
-
</YStack>
|
|
397
|
-
</Card.Footer>
|
|
398
|
-
</Card>
|
|
384
|
+
</YStack>
|
|
399
385
|
);
|
|
400
386
|
}
|
|
@@ -165,7 +165,8 @@ describe("FrappeListView controls write their state back", () => {
|
|
|
165
165
|
renderList({}, {}, navigationAt({}, setParams));
|
|
166
166
|
await waitFor(() => expect(shownCards().length).toBe(6), { timeout: 5000 });
|
|
167
167
|
const chip = await screen.findByTestId("pokemon-type-fire");
|
|
168
|
-
expect(
|
|
168
|
+
expect(chip.hasAttribute("data-chip")).toBe(true);
|
|
169
|
+
expect(chip.getAttribute("role")).toBe("radio");
|
|
169
170
|
fireEvent.click(chip);
|
|
170
171
|
await expectCards(["charmander"]);
|
|
171
172
|
expect(setParams).toHaveBeenCalledWith({ type: "fire" });
|
|
@@ -228,7 +229,7 @@ describe("FrappeListView routed page with its toolbar off", () => {
|
|
|
228
229
|
fireEvent.click(screen.getByTestId("insights"));
|
|
229
230
|
expect(navigate).toHaveBeenCalledWith("/pokemon/insights");
|
|
230
231
|
expect(screen.getByText("Skip to results")).toBeTruthy();
|
|
231
|
-
fireEvent.click(await screen.
|
|
232
|
+
fireEvent.click(await screen.findByRole("button", { name: "Add Pokemon" }));
|
|
232
233
|
expect(navigate).toHaveBeenCalledWith("/pokemon/new");
|
|
233
234
|
});
|
|
234
235
|
});
|
package/src/FrappeListView.tsx
CHANGED
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
EmptyState,
|
|
23
23
|
ListPageLayout,
|
|
24
24
|
ListRow,
|
|
25
|
+
PageBarButton,
|
|
25
26
|
PageHeader,
|
|
26
27
|
SkipLink,
|
|
27
28
|
Tooltip,
|
|
@@ -922,7 +923,7 @@ function FrappeListViewBody<TDoc extends { name: string; doctype: string }>({
|
|
|
922
923
|
: [];
|
|
923
924
|
const headerActions =
|
|
924
925
|
headerOwnsViews || (headerOwnsTools && onAdd) || headerAccessory || actions?.length ? (
|
|
925
|
-
<XStack
|
|
926
|
+
<XStack alignItems="center">
|
|
926
927
|
{headerOwnsViews ? (
|
|
927
928
|
<Tooltip content={t("Switch views")}>
|
|
928
929
|
<XStack {...secondaryHidden}>
|
|
@@ -938,69 +939,35 @@ function FrappeListViewBody<TDoc extends { name: string; doctype: string }>({
|
|
|
938
939
|
{actions?.map((action) => {
|
|
939
940
|
const Icon = action.icon;
|
|
940
941
|
const onPress = pressFor(action);
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
<
|
|
944
|
-
|
|
945
|
-
<
|
|
946
|
-
|
|
942
|
+
return (
|
|
943
|
+
<XStack key={action.label} {...secondaryHidden}>
|
|
944
|
+
<Tooltip content={action.hint ?? action.label}>
|
|
945
|
+
{Icon ? (
|
|
946
|
+
<PageBarButton
|
|
947
|
+
icon={Icon as never}
|
|
948
|
+
aria-label={action.label}
|
|
947
949
|
onPress={onPress}
|
|
948
|
-
icon={Icon ? <Icon size={16} /> : undefined}
|
|
949
950
|
testID={action.testID}
|
|
950
|
-
|
|
951
|
+
/>
|
|
952
|
+
) : (
|
|
953
|
+
<Button chromeless accentInk onPress={onPress} testID={action.testID}>
|
|
951
954
|
{action.label}
|
|
952
955
|
</Button>
|
|
953
|
-
|
|
954
|
-
</XStack>
|
|
955
|
-
);
|
|
956
|
-
}
|
|
957
|
-
return (
|
|
958
|
-
<React.Fragment key={action.label}>
|
|
959
|
-
<Tooltip content={action.hint ?? action.label}>
|
|
960
|
-
{/* responsive-parity: the circular icon twin below carries this action on phones */}
|
|
961
|
-
<Button
|
|
962
|
-
outlined
|
|
963
|
-
onPress={onPress}
|
|
964
|
-
icon={Icon ? <Icon size={16} /> : undefined}
|
|
965
|
-
$max-sm={{ display: "none" }}
|
|
966
|
-
testID={action.testID}
|
|
967
|
-
>
|
|
968
|
-
{action.label}
|
|
969
|
-
</Button>
|
|
956
|
+
)}
|
|
970
957
|
</Tooltip>
|
|
971
|
-
|
|
972
|
-
outlined
|
|
973
|
-
circular
|
|
974
|
-
onPress={onPress}
|
|
975
|
-
icon={Icon ? <Icon size={16} /> : undefined}
|
|
976
|
-
display="none"
|
|
977
|
-
aria-label={action.label}
|
|
978
|
-
$max-sm={{ display: "flex" }}
|
|
979
|
-
/>
|
|
980
|
-
</React.Fragment>
|
|
958
|
+
</XStack>
|
|
981
959
|
);
|
|
982
960
|
})}
|
|
983
961
|
{headerOwnsTools && onAdd ? (
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
onPress={onAdd}
|
|
989
|
-
icon={<PlusIcon size={16} />}
|
|
990
|
-
$max-sm={{ display: "none" }}
|
|
991
|
-
>
|
|
992
|
-
{t("Add {{doctype}}", { doctype })}
|
|
993
|
-
</Button>
|
|
994
|
-
<Button
|
|
995
|
-
theme="accent"
|
|
962
|
+
<Tooltip content={t("Add {{doctype}}", { doctype })}>
|
|
963
|
+
<PageBarButton
|
|
964
|
+
icon={PlusIcon}
|
|
965
|
+
aria-label={t("Add {{doctype}}", { doctype })}
|
|
996
966
|
onPress={onAdd}
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
{t("Add")}
|
|
1002
|
-
</Button>
|
|
1003
|
-
</>
|
|
967
|
+
edge="end"
|
|
968
|
+
testID={`${slug}-add`}
|
|
969
|
+
/>
|
|
970
|
+
</Tooltip>
|
|
1004
971
|
) : null}
|
|
1005
972
|
</XStack>
|
|
1006
973
|
) : undefined;
|
|
@@ -1013,6 +980,7 @@ function FrappeListViewBody<TDoc extends { name: string; doctype: string }>({
|
|
|
1013
980
|
rows={shuffleRows}
|
|
1014
981
|
itemLabel={shuffle.label}
|
|
1015
982
|
renderPick={(doc) => drawCard(doc, 0)}
|
|
983
|
+
grid={gridProps}
|
|
1016
984
|
onGo={(doc) => {
|
|
1017
985
|
setShuffleOpen(false);
|
|
1018
986
|
onItemClick?.(doc);
|
package/src/FrappeRecordView.tsx
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { EmptyState, Screen } from "@multiplatform.one/components";
|
|
2
2
|
import { Button } from "@multiplatform.one/forms";
|
|
3
3
|
import type { Order } from "@multiplatform.one/frappe";
|
|
4
|
+
import { DETAIL_READABLE_WIDTH } from "@multiplatform.one/theme";
|
|
4
5
|
import { useMemo } from "react";
|
|
5
6
|
import { useTranslation } from "react-i18next";
|
|
6
7
|
import type { FrappeConnection } from "./frappeConnections";
|
|
@@ -141,12 +142,18 @@ export function FrappeRecordView<TDoc extends { name: string; doctype: string }>
|
|
|
141
142
|
showSidebar={false}
|
|
142
143
|
showTimeline={false}
|
|
143
144
|
loadingFallback={
|
|
144
|
-
<Screen noPadding gap={0} topBar={backButton}>
|
|
145
|
+
<Screen noPadding gap={0} maxWidth={DETAIL_READABLE_WIDTH} topBar={backButton}>
|
|
145
146
|
<RecordSkeleton testID={`${slug}-detail-skeleton`} />
|
|
146
147
|
</Screen>
|
|
147
148
|
}
|
|
148
149
|
notFoundFallback={
|
|
149
|
-
<Screen
|
|
150
|
+
<Screen
|
|
151
|
+
scroll={false}
|
|
152
|
+
noPadding
|
|
153
|
+
gap={0}
|
|
154
|
+
maxWidth={DETAIL_READABLE_WIDTH}
|
|
155
|
+
topBar={backButton}
|
|
156
|
+
>
|
|
150
157
|
<EmptyState
|
|
151
158
|
title={t("{{doctype}} not found", { doctype })}
|
|
152
159
|
description={t("No {{doctype}} is named {{name}}.", { doctype, name })}
|
|
@@ -159,7 +166,13 @@ export function FrappeRecordView<TDoc extends { name: string; doctype: string }>
|
|
|
159
166
|
</Screen>
|
|
160
167
|
}
|
|
161
168
|
errorFallback={
|
|
162
|
-
<Screen
|
|
169
|
+
<Screen
|
|
170
|
+
scroll={false}
|
|
171
|
+
noPadding
|
|
172
|
+
gap={0}
|
|
173
|
+
maxWidth={DETAIL_READABLE_WIDTH}
|
|
174
|
+
topBar={backButton}
|
|
175
|
+
>
|
|
163
176
|
<EmptyState
|
|
164
177
|
intent="error"
|
|
165
178
|
title={t("Couldn't load this {{doctype}}", { doctype })}
|
|
@@ -870,25 +870,27 @@ function FrappeReportBuilderBody({
|
|
|
870
870
|
|
|
871
871
|
const content = (
|
|
872
872
|
<YStack flex={1} minWidth={0} minHeight={0} {...stackProps}>
|
|
873
|
-
<
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
873
|
+
<YStack paddingHorizontal="$3">
|
|
874
|
+
<ReportViewToolbar
|
|
875
|
+
columns={allColumns}
|
|
876
|
+
selected={selectedFieldnames}
|
|
877
|
+
onToggleColumn={handleToggleColumn}
|
|
878
|
+
onResetColumns={handleResetColumns}
|
|
879
|
+
groupBy={groupBy}
|
|
880
|
+
onGroupByChange={setGroupBy}
|
|
881
|
+
baseFilters={baseFilters}
|
|
882
|
+
onClearBaseFilters={baseFilters.length > 0 ? handleClearBaseFilters : undefined}
|
|
883
|
+
columnFilterCount={columnTuples.length}
|
|
884
|
+
onClearColumnFilters={handleClearColumnFilters}
|
|
885
|
+
savedReports={savedReports.reports}
|
|
886
|
+
currentReport={currentReport}
|
|
887
|
+
onSelectReport={handleSelectReport}
|
|
888
|
+
onSaveReport={readsWithoutMeta ? undefined : handleSaveReport}
|
|
889
|
+
onDeleteReport={readsWithoutMeta ? undefined : handleDeleteReport}
|
|
890
|
+
onExport={exportEnabled ? handleExport : undefined}
|
|
891
|
+
exportDisabled={total === 0}
|
|
892
|
+
/>
|
|
893
|
+
</YStack>
|
|
892
894
|
<ScrollView
|
|
893
895
|
flex={1}
|
|
894
896
|
minWidth={0}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SyncErrorNotice contract: the visible half of the rejected-write pattern —
|
|
2
|
-
// role="alert" message, Retry re-issues,
|
|
2
|
+
// role="alert" message, Retry re-issues, the close button clears, and each affordance
|
|
3
3
|
// only renders when its handler is provided.
|
|
4
4
|
import { renderWithProviders } from "@multiplatform.one/test-utils";
|
|
5
5
|
import { cleanup, fireEvent, screen } from "@testing-library/react";
|
|
@@ -33,7 +33,9 @@ describe("SyncErrorNotice", () => {
|
|
|
33
33
|
);
|
|
34
34
|
fireEvent.click(await screen.findByText("Retry"));
|
|
35
35
|
expect(onRetry).toHaveBeenCalledTimes(1);
|
|
36
|
-
|
|
36
|
+
const close = await screen.findByRole("button", { name: "Dismiss" });
|
|
37
|
+
expect(close.textContent?.trim()).toBe("");
|
|
38
|
+
fireEvent.click(close);
|
|
37
39
|
expect(onDismiss).toHaveBeenCalledTimes(1);
|
|
38
40
|
});
|
|
39
41
|
|
|
@@ -41,20 +43,13 @@ describe("SyncErrorNotice", () => {
|
|
|
41
43
|
renderWithProviders(<SyncErrorNotice message="No actions" />);
|
|
42
44
|
await screen.findByRole("alert");
|
|
43
45
|
expect(screen.queryByText("Retry")).toBeNull();
|
|
44
|
-
expect(screen.
|
|
46
|
+
expect(screen.queryByRole("button", { name: "Dismiss" })).toBeNull();
|
|
45
47
|
});
|
|
46
48
|
|
|
47
|
-
it("honors custom
|
|
49
|
+
it("honors a custom Retry label", async () => {
|
|
48
50
|
renderWithProviders(
|
|
49
|
-
<SyncErrorNotice
|
|
50
|
-
message="Custom"
|
|
51
|
-
onRetry={() => {}}
|
|
52
|
-
onDismiss={() => {}}
|
|
53
|
-
retryLabel="Try again"
|
|
54
|
-
dismissLabel="Ignore"
|
|
55
|
-
/>,
|
|
51
|
+
<SyncErrorNotice message="Custom" onRetry={() => {}} retryLabel="Try again" />,
|
|
56
52
|
);
|
|
57
53
|
expect(await screen.findByText("Try again")).toBeTruthy();
|
|
58
|
-
expect(await screen.findByText("Ignore")).toBeTruthy();
|
|
59
54
|
});
|
|
60
55
|
});
|
|
@@ -10,14 +10,13 @@ export default {
|
|
|
10
10
|
docs: {
|
|
11
11
|
description: {
|
|
12
12
|
component:
|
|
13
|
-
"Rejected-write notice: role=alert message with Retry (same mutation id) and
|
|
13
|
+
"Rejected-write notice: role=alert message with Retry (same mutation id) and a close button. Each affordance only renders when its handler is provided.",
|
|
14
14
|
},
|
|
15
15
|
},
|
|
16
16
|
},
|
|
17
17
|
argTypes: {
|
|
18
18
|
message: { control: "text" },
|
|
19
19
|
retryLabel: { control: "text" },
|
|
20
|
-
dismissLabel: { control: "text" },
|
|
21
20
|
},
|
|
22
21
|
};
|
|
23
22
|
|
|
@@ -40,11 +39,10 @@ export const AllStates = () => (
|
|
|
40
39
|
<SyncErrorNotice message="Write rejected — retry only" onRetry={action("onRetry")} />
|
|
41
40
|
<SyncErrorNotice message="No actions" />
|
|
42
41
|
<SyncErrorNotice
|
|
43
|
-
message="Custom
|
|
42
|
+
message="Custom Retry label"
|
|
44
43
|
onRetry={action("onRetry")}
|
|
45
44
|
onDismiss={action("onDismiss")}
|
|
46
45
|
retryLabel="Try again"
|
|
47
|
-
dismissLabel="Ignore"
|
|
48
46
|
/>
|
|
49
47
|
</YStack>
|
|
50
48
|
);
|
package/src/SyncErrorNotice.tsx
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { useResolvedKnobs } from "@multiplatform.one/theme";
|
|
1
|
+
import { Alert } from "@multiplatform.one/components";
|
|
3
2
|
import React from "react";
|
|
4
3
|
import { useTranslation } from "react-i18next";
|
|
5
|
-
import { XStack } from "tamagui";
|
|
6
4
|
|
|
7
5
|
export interface SyncErrorNoticeProps {
|
|
8
6
|
/** Human-readable failure message (already localized by the caller). */
|
|
@@ -12,20 +10,18 @@ export interface SyncErrorNoticeProps {
|
|
|
12
10
|
* Omit to hide the Retry affordance.
|
|
13
11
|
*/
|
|
14
12
|
onRetry?: () => void;
|
|
15
|
-
/** Drops the failed entry without retrying. Omit to hide
|
|
13
|
+
/** Drops the failed entry without retrying: the alert's close button. Omit to hide it. */
|
|
16
14
|
onDismiss?: () => void;
|
|
17
15
|
/** Override the Retry button label (defaults to a localized "Retry"). */
|
|
18
16
|
retryLabel?: string;
|
|
19
|
-
/** Override the Dismiss button label (defaults to a localized "Dismiss"). */
|
|
20
|
-
dismissLabel?: string;
|
|
21
17
|
}
|
|
22
18
|
|
|
23
19
|
/**
|
|
24
20
|
* The rejected-write notice — the visible half of the optimistic mutation
|
|
25
|
-
* contract: when the server rejects a write, the optimistic state has
|
|
21
|
+
* contract, on the MPO error `Alert` (LC-175): when the server rejects a write, the optimistic state has
|
|
26
22
|
* already been rolled back by the sync engine; this primitive surfaces the
|
|
27
23
|
* error inline at the surface that initiated the write, with Retry
|
|
28
|
-
* (re-issues the SAME mutation id) and
|
|
24
|
+
* (re-issues the SAME mutation id) and a close button (clears the ledger entry).
|
|
29
25
|
*
|
|
30
26
|
* Pair with `useFrappePendingMutations` from `@multiplatform.one/frappe`:
|
|
31
27
|
*
|
|
@@ -41,41 +37,17 @@ export interface SyncErrorNoticeProps {
|
|
|
41
37
|
* ))}
|
|
42
38
|
* ```
|
|
43
39
|
*/
|
|
44
|
-
export function SyncErrorNotice({
|
|
45
|
-
message,
|
|
46
|
-
onRetry,
|
|
47
|
-
onDismiss,
|
|
48
|
-
retryLabel,
|
|
49
|
-
dismissLabel,
|
|
50
|
-
}: SyncErrorNoticeProps) {
|
|
40
|
+
export function SyncErrorNotice({ message, onRetry, onDismiss, retryLabel }: SyncErrorNoticeProps) {
|
|
51
41
|
const { t } = useTranslation();
|
|
52
|
-
const { knobProps } = useResolvedKnobs();
|
|
53
42
|
return (
|
|
54
|
-
<
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
paddingVertical="$2"
|
|
61
|
-
{...knobProps.borderRadius}
|
|
62
|
-
backgroundColor="$red3"
|
|
63
|
-
borderColor="$red7"
|
|
64
|
-
borderWidth={1}
|
|
43
|
+
<Alert
|
|
44
|
+
intent="error"
|
|
45
|
+
actionLabel={onRetry ? (retryLabel ?? t("Retry")) : undefined}
|
|
46
|
+
onAction={onRetry}
|
|
47
|
+
dismissible={Boolean(onDismiss)}
|
|
48
|
+
onDismiss={onDismiss}
|
|
65
49
|
>
|
|
66
|
-
|
|
67
|
-
|
|
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>
|
|
50
|
+
{message}
|
|
51
|
+
</Alert>
|
|
80
52
|
);
|
|
81
53
|
}
|
|
@@ -20,7 +20,6 @@ import { useDocTypeMeta, useFrappeCollection, useLiveQuery } from "@multiplatfor
|
|
|
20
20
|
import { eq } from "@tanstack/db";
|
|
21
21
|
import React, { useMemo } from "react";
|
|
22
22
|
import { useTranslation } from "react-i18next";
|
|
23
|
-
import { isWeb } from "tamagui";
|
|
24
23
|
import {
|
|
25
24
|
buildSidebarSections,
|
|
26
25
|
mapDocToSidebarMetadata,
|
|
@@ -142,7 +141,7 @@ export function DeskFormSidebar({ doctype, name }: DeskFormSidebarProps) {
|
|
|
142
141
|
// inline rail width, and drop the end rail — the pane gap separates the
|
|
143
142
|
// sidebar from the form (stacked below on compact, the rail would float).
|
|
144
143
|
width="100%"
|
|
145
|
-
{
|
|
144
|
+
divider={false}
|
|
146
145
|
data-testid="desk-form-sidebar"
|
|
147
146
|
/>
|
|
148
147
|
);
|
package/src/desk/DeskShell.tsx
CHANGED
|
@@ -90,6 +90,7 @@ function useIsNarrowDesk(): boolean {
|
|
|
90
90
|
// Component
|
|
91
91
|
// ---------------------------------------------------------------------------
|
|
92
92
|
|
|
93
|
+
// mpo-role-escape: MPO has no app shell (top bar, nav rail, compact drawer) yet; FZ-7996 builds it and moves DeskShell onto it.
|
|
93
94
|
export function DeskShell({
|
|
94
95
|
children,
|
|
95
96
|
breadcrumbs,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* The `Date` field for the three Frappe date fieldtypes, on web and native.
|
|
3
|
+
* On iOS and Android the forms pickers open the OS sheet themselves
|
|
4
|
+
* (`OsDateTimeSheet`), so this field has no native fork (FZ-8000).
|
|
4
5
|
*
|
|
5
6
|
* The field used to render ONE date-only calendar for all three types, with
|
|
6
7
|
* only the placeholder varying — so a `Datetime` silently dropped its time
|
|
@@ -14,8 +15,8 @@
|
|
|
14
15
|
* registry routes the Frappe `Time` fieldtype to, so a
|
|
15
16
|
* `Date type="time"` and a `Time` field are one control
|
|
16
17
|
*
|
|
17
|
-
*
|
|
18
|
-
* calendar-then-wheel
|
|
18
|
+
* On native the same three pickers present the OS calendar, wheel, and
|
|
19
|
+
* calendar-then-wheel, and every branch serializes through the one shared
|
|
19
20
|
* `wire.ts` body, so a web pick and a native pick of the same moment persist
|
|
20
21
|
* byte-identical strings.
|
|
21
22
|
*/
|
|
@@ -50,8 +51,7 @@ function toPickerDate(value: string | undefined): Date | null {
|
|
|
50
51
|
/**
|
|
51
52
|
* `TimePicker` speaks `HH:mm`; Frappe stores `HH:mm:ss`. Round-tripping
|
|
52
53
|
* through the shared wire body — rather than padding the string here — is
|
|
53
|
-
* what keeps
|
|
54
|
-
* serializes its wheel pick through exactly this call.
|
|
54
|
+
* what keeps a picked time in Frappe's wire shape on every platform.
|
|
55
55
|
*/
|
|
56
56
|
function toTimeWireValue(picked: string): string {
|
|
57
57
|
if (!picked) return "";
|
|
@@ -130,7 +130,6 @@ export function Date<
|
|
|
130
130
|
// No `previousValue`: this picker owns both halves of the value and
|
|
131
131
|
// preserves an existing time across a day change itself, so carrying
|
|
132
132
|
// the old time part here would overwrite a time the user just set.
|
|
133
|
-
// The native half passes nothing for the same reason.
|
|
134
133
|
onChange={(date) => emit?.(date ? toFrappeDateValue(date, "datetime") : "")}
|
|
135
134
|
defaultValue={defaultValue as string}
|
|
136
135
|
label={fieldProps.label}
|