@multiplatform.one/forms 7.67.0 → 7.69.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/AdaptivePopup/index.cjs +45 -5
- package/dist/cjs/AdaptivePopup/index.native.js +47 -5
- package/dist/cjs/AdaptivePopup/index.native.js.map +1 -1
- package/dist/cjs/Button/IconButtonGroup.cjs +95 -0
- package/dist/cjs/Button/IconButtonGroup.native.js +101 -0
- package/dist/cjs/Button/IconButtonGroup.native.js.map +1 -0
- package/dist/cjs/Button/glass.cjs +34 -0
- package/dist/cjs/Button/glass.native.js +55 -0
- package/dist/cjs/Button/glass.native.js.map +1 -0
- package/dist/cjs/Button/iconButtonGroupContext.cjs +33 -0
- package/dist/cjs/Button/iconButtonGroupContext.native.js +36 -0
- package/dist/cjs/Button/iconButtonGroupContext.native.js.map +1 -0
- package/dist/cjs/Button/index.cjs +174 -30
- package/dist/cjs/Button/index.native.js +174 -29
- package/dist/cjs/Button/index.native.js.map +1 -1
- package/dist/cjs/Button/mediaPlate.cjs +31 -0
- package/dist/cjs/Button/mediaPlate.native.js +34 -0
- package/dist/cjs/Button/mediaPlate.native.js.map +1 -0
- package/dist/cjs/ContextualSaveBar/index.cjs +6 -8
- package/dist/cjs/ContextualSaveBar/index.native.js +6 -8
- package/dist/cjs/ContextualSaveBar/index.native.js.map +1 -1
- package/dist/cjs/DescriptionList/index.cjs +0 -1
- package/dist/cjs/DescriptionList/index.native.js +0 -1
- package/dist/cjs/DescriptionList/index.native.js.map +1 -1
- package/dist/cjs/FloatingPanel/index.native.js +21 -16
- package/dist/cjs/FloatingPanel/index.native.js.map +1 -1
- package/dist/cjs/fields/Barcode/BarcodeScanner.cjs +7 -8
- package/dist/cjs/fields/Barcode/BarcodeScanner.native.js +7 -8
- package/dist/cjs/fields/Barcode/BarcodeScanner.native.js.map +1 -1
- package/dist/cjs/fields/FileUpload/index.cjs +86 -60
- package/dist/cjs/fields/FileUpload/index.native.js +99 -73
- package/dist/cjs/fields/FileUpload/index.native.js.map +1 -1
- package/dist/cjs/fields/Geolocation/index.cjs +7 -8
- package/dist/cjs/fields/Geolocation/index.native.js +7 -8
- package/dist/cjs/fields/Geolocation/index.native.js.map +1 -1
- package/dist/cjs/fields/ImageField/index.cjs +4 -6
- package/dist/cjs/fields/ImageField/index.native.js +4 -6
- package/dist/cjs/fields/ImageField/index.native.js.map +1 -1
- package/dist/cjs/fields/nativeDateTime/OsDateTimeSheet.cjs +20 -28
- package/dist/cjs/fields/nativeDateTime/OsDateTimeSheet.native.js +20 -28
- package/dist/cjs/fields/nativeDateTime/OsDateTimeSheet.native.js.map +1 -1
- package/dist/cjs/index.cjs +11 -1
- package/dist/cjs/index.native.js +10 -0
- package/dist/cjs/index.native.js.map +1 -1
- package/dist/cjs/shared/colorRamps.cjs +0 -8
- package/dist/cjs/shared/colorRamps.native.js +0 -8
- package/dist/cjs/shared/colorRamps.native.js.map +1 -1
- package/dist/cjs/standardActions.cjs +117 -0
- package/dist/cjs/standardActions.native.js +120 -0
- package/dist/cjs/standardActions.native.js.map +1 -0
- package/dist/esm/AdaptivePopup/index.mjs +45 -5
- package/dist/esm/AdaptivePopup/index.mjs.map +1 -1
- package/dist/esm/AdaptivePopup/index.native.js +47 -5
- package/dist/esm/AdaptivePopup/index.native.js.map +1 -1
- package/dist/esm/Button/IconButtonGroup.mjs +69 -0
- package/dist/esm/Button/IconButtonGroup.mjs.map +1 -0
- package/dist/esm/Button/IconButtonGroup.native.js +72 -0
- package/dist/esm/Button/IconButtonGroup.native.js.map +1 -0
- package/dist/esm/Button/glass.mjs +8 -0
- package/dist/esm/Button/glass.mjs.map +1 -0
- package/dist/esm/Button/glass.native.js +26 -0
- package/dist/esm/Button/glass.native.js.map +1 -0
- package/dist/esm/Button/iconButtonGroupContext.mjs +7 -0
- package/dist/esm/Button/iconButtonGroupContext.mjs.map +1 -0
- package/dist/esm/Button/iconButtonGroupContext.native.js +7 -0
- package/dist/esm/Button/iconButtonGroupContext.native.js.map +1 -0
- package/dist/esm/Button/index.mjs +166 -30
- package/dist/esm/Button/index.mjs.map +1 -1
- package/dist/esm/Button/index.native.js +167 -30
- package/dist/esm/Button/index.native.js.map +1 -1
- package/dist/esm/Button/mediaPlate.mjs +6 -0
- package/dist/esm/Button/mediaPlate.mjs.map +1 -0
- package/dist/esm/Button/mediaPlate.native.js +6 -0
- package/dist/esm/Button/mediaPlate.native.js.map +1 -0
- package/dist/esm/ContextualSaveBar/index.mjs +7 -9
- package/dist/esm/ContextualSaveBar/index.mjs.map +1 -1
- package/dist/esm/ContextualSaveBar/index.native.js +7 -9
- package/dist/esm/ContextualSaveBar/index.native.js.map +1 -1
- package/dist/esm/DescriptionList/index.mjs +0 -1
- package/dist/esm/DescriptionList/index.mjs.map +1 -1
- package/dist/esm/DescriptionList/index.native.js +0 -1
- package/dist/esm/DescriptionList/index.native.js.map +1 -1
- package/dist/esm/FloatingPanel/index.native.js +22 -17
- package/dist/esm/FloatingPanel/index.native.js.map +1 -1
- package/dist/esm/fields/Barcode/BarcodeScanner.mjs +8 -9
- package/dist/esm/fields/Barcode/BarcodeScanner.mjs.map +1 -1
- package/dist/esm/fields/Barcode/BarcodeScanner.native.js +8 -9
- package/dist/esm/fields/Barcode/BarcodeScanner.native.js.map +1 -1
- package/dist/esm/fields/FileUpload/index.mjs +87 -61
- package/dist/esm/fields/FileUpload/index.mjs.map +1 -1
- package/dist/esm/fields/FileUpload/index.native.js +100 -74
- package/dist/esm/fields/FileUpload/index.native.js.map +1 -1
- package/dist/esm/fields/Geolocation/index.mjs +8 -9
- package/dist/esm/fields/Geolocation/index.mjs.map +1 -1
- package/dist/esm/fields/Geolocation/index.native.js +8 -9
- package/dist/esm/fields/Geolocation/index.native.js.map +1 -1
- package/dist/esm/fields/ImageField/index.mjs +6 -8
- package/dist/esm/fields/ImageField/index.mjs.map +1 -1
- package/dist/esm/fields/ImageField/index.native.js +6 -8
- package/dist/esm/fields/ImageField/index.native.js.map +1 -1
- package/dist/esm/fields/nativeDateTime/OsDateTimeSheet.mjs +20 -28
- package/dist/esm/fields/nativeDateTime/OsDateTimeSheet.mjs.map +1 -1
- package/dist/esm/fields/nativeDateTime/OsDateTimeSheet.native.js +20 -28
- package/dist/esm/fields/nativeDateTime/OsDateTimeSheet.native.js.map +1 -1
- package/dist/esm/index.mjs +3 -2
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/index.native.js +3 -2
- package/dist/esm/index.native.js.map +1 -1
- package/dist/esm/shared/colorRamps.mjs +0 -8
- package/dist/esm/shared/colorRamps.mjs.map +1 -1
- package/dist/esm/shared/colorRamps.native.js +0 -8
- package/dist/esm/shared/colorRamps.native.js.map +1 -1
- package/dist/esm/standardActions.mjs +91 -0
- package/dist/esm/standardActions.mjs.map +1 -0
- package/dist/esm/standardActions.native.js +91 -0
- package/dist/esm/standardActions.native.js.map +1 -0
- package/dist/jsx/AdaptivePopup/index.mjs +45 -5
- package/dist/jsx/AdaptivePopup/index.mjs.map +1 -1
- package/dist/jsx/AdaptivePopup/index.native.js +47 -5
- package/dist/jsx/AdaptivePopup/index.native.js.map +1 -1
- package/dist/jsx/Button/IconButtonGroup.mjs +69 -0
- package/dist/jsx/Button/IconButtonGroup.mjs.map +1 -0
- package/dist/jsx/Button/IconButtonGroup.native.js +101 -0
- package/dist/jsx/Button/IconButtonGroup.native.js.map +1 -0
- package/dist/jsx/Button/glass.mjs +8 -0
- package/dist/jsx/Button/glass.mjs.map +1 -0
- package/dist/jsx/Button/glass.native.js +55 -0
- package/dist/jsx/Button/glass.native.js.map +1 -0
- package/dist/jsx/Button/iconButtonGroupContext.mjs +7 -0
- package/dist/jsx/Button/iconButtonGroupContext.mjs.map +1 -0
- package/dist/jsx/Button/iconButtonGroupContext.native.js +36 -0
- package/dist/jsx/Button/iconButtonGroupContext.native.js.map +1 -0
- package/dist/jsx/Button/index.mjs +166 -30
- package/dist/jsx/Button/index.mjs.map +1 -1
- package/dist/jsx/Button/index.native.js +174 -29
- package/dist/jsx/Button/index.native.js.map +1 -1
- package/dist/jsx/Button/mediaPlate.mjs +6 -0
- package/dist/jsx/Button/mediaPlate.mjs.map +1 -0
- package/dist/jsx/Button/mediaPlate.native.js +34 -0
- package/dist/jsx/Button/mediaPlate.native.js.map +1 -0
- package/dist/jsx/ContextualSaveBar/index.mjs +7 -9
- package/dist/jsx/ContextualSaveBar/index.mjs.map +1 -1
- package/dist/jsx/ContextualSaveBar/index.native.js +6 -8
- package/dist/jsx/ContextualSaveBar/index.native.js.map +1 -1
- package/dist/jsx/DescriptionList/index.mjs +0 -1
- package/dist/jsx/DescriptionList/index.mjs.map +1 -1
- package/dist/jsx/DescriptionList/index.native.js +0 -1
- package/dist/jsx/DescriptionList/index.native.js.map +1 -1
- package/dist/jsx/FloatingPanel/index.native.js +21 -16
- package/dist/jsx/FloatingPanel/index.native.js.map +1 -1
- package/dist/jsx/fields/Barcode/BarcodeScanner.mjs +8 -9
- package/dist/jsx/fields/Barcode/BarcodeScanner.mjs.map +1 -1
- package/dist/jsx/fields/Barcode/BarcodeScanner.native.js +7 -8
- package/dist/jsx/fields/Barcode/BarcodeScanner.native.js.map +1 -1
- package/dist/jsx/fields/FileUpload/index.mjs +87 -61
- package/dist/jsx/fields/FileUpload/index.mjs.map +1 -1
- package/dist/jsx/fields/FileUpload/index.native.js +99 -73
- package/dist/jsx/fields/FileUpload/index.native.js.map +1 -1
- package/dist/jsx/fields/Geolocation/index.mjs +8 -9
- package/dist/jsx/fields/Geolocation/index.mjs.map +1 -1
- package/dist/jsx/fields/Geolocation/index.native.js +7 -8
- package/dist/jsx/fields/Geolocation/index.native.js.map +1 -1
- package/dist/jsx/fields/ImageField/index.mjs +6 -8
- package/dist/jsx/fields/ImageField/index.mjs.map +1 -1
- package/dist/jsx/fields/ImageField/index.native.js +4 -6
- package/dist/jsx/fields/ImageField/index.native.js.map +1 -1
- package/dist/jsx/fields/nativeDateTime/OsDateTimeSheet.mjs +20 -28
- package/dist/jsx/fields/nativeDateTime/OsDateTimeSheet.mjs.map +1 -1
- package/dist/jsx/fields/nativeDateTime/OsDateTimeSheet.native.js +20 -28
- package/dist/jsx/fields/nativeDateTime/OsDateTimeSheet.native.js.map +1 -1
- package/dist/jsx/index.js +3 -2
- package/dist/jsx/index.js.map +1 -1
- package/dist/jsx/index.mjs +3 -2
- package/dist/jsx/index.mjs.map +1 -1
- package/dist/jsx/index.native.js +10 -0
- package/dist/jsx/index.native.js.map +1 -1
- package/dist/jsx/shared/colorRamps.mjs +0 -8
- package/dist/jsx/shared/colorRamps.mjs.map +1 -1
- package/dist/jsx/shared/colorRamps.native.js +0 -8
- package/dist/jsx/shared/colorRamps.native.js.map +1 -1
- package/dist/jsx/standardActions.mjs +91 -0
- package/dist/jsx/standardActions.mjs.map +1 -0
- package/dist/jsx/standardActions.native.js +120 -0
- package/dist/jsx/standardActions.native.js.map +1 -0
- package/package.json +12 -8
- package/src/AdaptivePopup/AdaptivePopup.spec.tsx +44 -0
- package/src/AdaptivePopup/AdaptivePopup.stories.tsx +12 -32
- package/src/AdaptivePopup/index.tsx +65 -13
- package/src/Button/Button.disabledGlyph.spec.tsx +111 -0
- package/src/Button/Button.iconButton.spec.tsx +341 -0
- package/src/Button/Button.stories.tsx +179 -2
- package/src/Button/IconButtonGroup.spec.tsx +126 -0
- package/src/Button/IconButtonGroup.tsx +87 -0
- package/src/Button/glass.native.ts +51 -0
- package/src/Button/glass.ts +33 -0
- package/src/Button/iconButtonGroupContext.ts +14 -0
- package/src/Button/index.tsx +314 -42
- package/src/Button/mediaPlate.ts +9 -0
- package/src/ContextualSaveBar/ContextualSaveBar.spec.tsx +3 -1
- package/src/ContextualSaveBar/ContextualSaveBar.stories.tsx +2 -2
- package/src/ContextualSaveBar/index.tsx +7 -10
- package/src/ControlGroup/ControlGroup.stories.tsx +3 -2
- package/src/DescriptionList/index.tsx +0 -1
- package/src/ErrorSummary.stories.tsx +2 -1
- package/src/FieldLayout.stories.tsx +3 -2
- package/src/FloatingPanel/FloatingPanel.pointMenu.native.spec.tsx +69 -0
- package/src/FloatingPanel/FloatingPanel.stories.tsx +6 -10
- package/src/FloatingPanel/index.native.tsx +25 -4
- package/src/Form/Form.stories.tsx +6 -5
- package/src/errorSummaryStories.spec.tsx +4 -4
- package/src/fields/Barcode/BarcodeScanner.native.tsx +10 -9
- package/src/fields/Barcode/BarcodeScanner.tsx +10 -9
- package/src/fields/Checkbox/Checkbox.stories.tsx +3 -3
- package/src/fields/Checkbox/CheckboxGroup.stories.tsx +2 -2
- package/src/fields/ColorPicker/ColorPicker.stories.tsx +2 -2
- package/src/fields/Combobox/Combobox.stories.tsx +2 -2
- package/src/fields/DatePicker/DatePicker.stories.tsx +2 -1
- package/src/fields/DatePicker/DateRangePicker.stories.tsx +2 -2
- package/src/fields/DatePicker/DatetimePicker.stories.tsx +2 -1
- package/src/fields/DatePicker/MonthPicker.stories.tsx +2 -2
- package/src/fields/DatePicker/MultiDatePicker.stories.tsx +2 -2
- package/src/fields/FileUpload/FileUpload.spec.tsx +14 -5
- package/src/fields/FileUpload/FileUpload.stories.tsx +2 -2
- package/src/fields/FileUpload/index.tsx +103 -77
- package/src/fields/Geolocation/index.tsx +12 -7
- package/src/fields/ImageField/ImageField.spec.tsx +5 -5
- package/src/fields/ImageField/ImageField.stories.tsx +2 -2
- package/src/fields/ImageField/index.tsx +6 -9
- package/src/fields/Input/Input.stories.tsx +2 -2
- package/src/fields/MentionInput/MentionInput.stories.tsx +2 -1
- package/src/fields/Progress/Progress.stories.tsx +2 -1
- package/src/fields/RadioGroup/RadioGroup.stories.tsx +2 -1
- package/src/fields/Signature/Signature.stories.tsx +2 -2
- package/src/fields/Stepper/Stepper.stories.tsx +2 -2
- package/src/fields/TextArea/TextArea.stories.tsx +2 -2
- package/src/fields/ToggleGroup/ToggleGroup.stories.tsx +2 -2
- package/src/fields/nativeDateTime/OsDateTimeSheet.presentation.spec.tsx +6 -5
- package/src/fields/nativeDateTime/OsDateTimeSheet.tsx +24 -12
- package/src/index.ts +18 -2
- package/src/shared/colorRamps.ts +1 -11
- package/src/standardActions.ts +96 -0
- package/types/AdaptivePopup/index.d.ts +13 -1
- package/types/AdaptivePopup/index.d.ts.map +1 -1
- package/types/Button/IconButtonGroup.d.ts +22 -0
- package/types/Button/IconButtonGroup.d.ts.map +1 -0
- package/types/Button/glass.d.ts +26 -0
- package/types/Button/glass.d.ts.map +1 -0
- package/types/Button/glass.native.d.ts +17 -0
- package/types/Button/glass.native.d.ts.map +1 -0
- package/types/Button/iconButtonGroupContext.d.ts +9 -0
- package/types/Button/iconButtonGroupContext.d.ts.map +1 -0
- package/types/Button/index.d.ts +55 -2
- package/types/Button/index.d.ts.map +1 -1
- package/types/Button/mediaPlate.d.ts +10 -0
- package/types/Button/mediaPlate.d.ts.map +1 -0
- package/types/ContextualSaveBar/index.d.ts.map +1 -1
- package/types/DescriptionList/index.d.ts.map +1 -1
- package/types/FloatingPanel/index.native.d.ts +2 -1
- package/types/FloatingPanel/index.native.d.ts.map +1 -1
- package/types/fields/Barcode/BarcodeScanner.d.ts.map +1 -1
- package/types/fields/Barcode/BarcodeScanner.native.d.ts.map +1 -1
- package/types/fields/FileUpload/index.d.ts.map +1 -1
- package/types/fields/Geolocation/index.d.ts.map +1 -1
- package/types/fields/ImageField/index.d.ts.map +1 -1
- package/types/fields/nativeDateTime/OsDateTimeSheet.d.ts.map +1 -1
- package/types/index.d.ts +4 -2
- package/types/index.d.ts.map +1 -1
- package/types/shared/colorRamps.d.ts +1 -11
- package/types/shared/colorRamps.d.ts.map +1 -1
- package/types/standardActions.d.ts +24 -0
- package/types/standardActions.d.ts.map +1 -0
|
@@ -6,9 +6,24 @@ import {
|
|
|
6
6
|
FOCUS_RING_MIN_CONTRAST,
|
|
7
7
|
ensureFocusVisibleRing,
|
|
8
8
|
Paragraph,
|
|
9
|
+
Surface,
|
|
9
10
|
} from "@multiplatform.one/theme";
|
|
10
11
|
import { Button as TamaguiButton, XStack, YStack, useTheme } from "tamagui";
|
|
11
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
ArchiveIcon,
|
|
14
|
+
ArrowCircleUpIcon,
|
|
15
|
+
ArrowsClockwiseIcon,
|
|
16
|
+
CaretLeftIcon,
|
|
17
|
+
CopyIcon,
|
|
18
|
+
DotsThreeIcon,
|
|
19
|
+
DownloadSimpleIcon,
|
|
20
|
+
EnvelopeSimpleIcon,
|
|
21
|
+
HeartIcon,
|
|
22
|
+
PlusIcon,
|
|
23
|
+
TrashIcon,
|
|
24
|
+
XIcon,
|
|
25
|
+
} from "@phosphor-icons/react";
|
|
26
|
+
import { Button, IconButton, IconButtonGroup, type ButtonProps } from "./index";
|
|
12
27
|
import { useState } from "react";
|
|
13
28
|
|
|
14
29
|
const pressed = (name: string) => () => action(name)();
|
|
@@ -411,10 +426,172 @@ export const DisabledReasons: Story = {
|
|
|
411
426
|
Publish
|
|
412
427
|
</Button>
|
|
413
428
|
<Button disabled disabledReason="No saved changes" disabledReasonPlacement={placement}>
|
|
414
|
-
Save
|
|
429
|
+
Save draft
|
|
415
430
|
</Button>
|
|
416
431
|
</YStack>
|
|
417
432
|
))}
|
|
418
433
|
</YStack>
|
|
419
434
|
),
|
|
420
435
|
};
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* LC-176 ICON-BUTTON, by context. In a bar the glyph takes the bar tint (on
|
|
439
|
+
* Liquid Glass on iOS 26); in content with no intent it is a bold glyph in the
|
|
440
|
+
* text colour; an intent keeps its ink; a selected toggle keeps LC-131; inline
|
|
441
|
+
* beside a value it is the text's size; over media it rides the one plate.
|
|
442
|
+
*/
|
|
443
|
+
export const IconButtons: Story = {
|
|
444
|
+
render: () => {
|
|
445
|
+
const [on, setOn] = useState(false);
|
|
446
|
+
return (
|
|
447
|
+
<YStack gap="$4" padding="$4" backgroundColor="$background">
|
|
448
|
+
<Surface bar>
|
|
449
|
+
<XStack alignItems="center" data-testid="icon-button-bar">
|
|
450
|
+
<IconButton icon={CaretLeftIcon} aria-label="Back" onPress={pressed("back")} />
|
|
451
|
+
<Paragraph flex={1} fontWeight="600">
|
|
452
|
+
Inbox
|
|
453
|
+
</Paragraph>
|
|
454
|
+
<IconButtonGroup data-testid="icon-button-toolbar">
|
|
455
|
+
<IconButton icon={ArchiveIcon} aria-label="Archive" onPress={pressed("archive")} />
|
|
456
|
+
<IconButton icon={TrashIcon} aria-label="Delete" onPress={pressed("delete")} />
|
|
457
|
+
<IconButton
|
|
458
|
+
icon={EnvelopeSimpleIcon}
|
|
459
|
+
aria-label="Mark unread"
|
|
460
|
+
selected={on}
|
|
461
|
+
onPress={() => setOn((v) => !v)}
|
|
462
|
+
/>
|
|
463
|
+
<IconButton icon={DotsThreeIcon} aria-label="More" onPress={pressed("more")} />
|
|
464
|
+
</IconButtonGroup>
|
|
465
|
+
</XStack>
|
|
466
|
+
</Surface>
|
|
467
|
+
<Surface bar>
|
|
468
|
+
<XStack alignItems="center" data-testid="icon-button-bar-disabled">
|
|
469
|
+
<Paragraph flex={1}>Downloads</Paragraph>
|
|
470
|
+
<IconButtonGroup>
|
|
471
|
+
<IconButton
|
|
472
|
+
icon={DownloadSimpleIcon}
|
|
473
|
+
aria-label="Download"
|
|
474
|
+
onPress={pressed("download")}
|
|
475
|
+
/>
|
|
476
|
+
<IconButton
|
|
477
|
+
icon={DownloadSimpleIcon}
|
|
478
|
+
aria-label="Download (nothing to download)"
|
|
479
|
+
disabled
|
|
480
|
+
/>
|
|
481
|
+
</IconButtonGroup>
|
|
482
|
+
</XStack>
|
|
483
|
+
</Surface>
|
|
484
|
+
<XStack alignItems="center" data-testid="icon-button-content">
|
|
485
|
+
<Paragraph flex={1}>A row in content</Paragraph>
|
|
486
|
+
<IconButtonGroup>
|
|
487
|
+
<IconButton
|
|
488
|
+
icon={DownloadSimpleIcon}
|
|
489
|
+
aria-label="Download"
|
|
490
|
+
onPress={pressed("download")}
|
|
491
|
+
/>
|
|
492
|
+
<IconButton icon={TrashIcon} error aria-label="Delete" onPress={pressed("delete")} />
|
|
493
|
+
</IconButtonGroup>
|
|
494
|
+
</XStack>
|
|
495
|
+
<XStack alignItems="center" data-testid="icon-button-inline">
|
|
496
|
+
<Paragraph>shc node join</Paragraph>
|
|
497
|
+
<IconButton inline icon={CopyIcon} aria-label="Copy" onPress={pressed("copy")} />
|
|
498
|
+
</XStack>
|
|
499
|
+
<YStack
|
|
500
|
+
position="relative"
|
|
501
|
+
width="$20"
|
|
502
|
+
height="$12"
|
|
503
|
+
backgroundColor="$blue9"
|
|
504
|
+
borderRadius="$4"
|
|
505
|
+
data-testid="icon-button-media"
|
|
506
|
+
>
|
|
507
|
+
<YStack position="absolute" top="$2" left="$2">
|
|
508
|
+
<IconButton
|
|
509
|
+
plate="media"
|
|
510
|
+
icon={HeartIcon}
|
|
511
|
+
aria-label="Favourite"
|
|
512
|
+
onPress={pressed("favourite")}
|
|
513
|
+
/>
|
|
514
|
+
</YStack>
|
|
515
|
+
<YStack position="absolute" top="$2" right="$2">
|
|
516
|
+
<IconButtonGroup plate="media">
|
|
517
|
+
<IconButton
|
|
518
|
+
plate="media"
|
|
519
|
+
icon={ArrowsClockwiseIcon}
|
|
520
|
+
aria-label="Replace photo"
|
|
521
|
+
onPress={pressed("replace")}
|
|
522
|
+
/>
|
|
523
|
+
<IconButton
|
|
524
|
+
plate="media"
|
|
525
|
+
icon={XIcon}
|
|
526
|
+
aria-label="Remove photo"
|
|
527
|
+
onPress={pressed("remove")}
|
|
528
|
+
/>
|
|
529
|
+
</IconButtonGroup>
|
|
530
|
+
</YStack>
|
|
531
|
+
</YStack>
|
|
532
|
+
</YStack>
|
|
533
|
+
);
|
|
534
|
+
},
|
|
535
|
+
};
|
|
536
|
+
|
|
537
|
+
// The SHC console's call sites as written (features/console place.tsx and
|
|
538
|
+
// CopyButton.tsx), spread so the design lint's `circular` finding stays on
|
|
539
|
+
// SHC's own code rather than this reproduction.
|
|
540
|
+
const shcRoundGlyph = { circular: true, chromeless: true, accentInk: true } as const;
|
|
541
|
+
|
|
542
|
+
/** Clay's SHC Nodes screen (IMG_1925 on FZ-8020): the add and the copy. */
|
|
543
|
+
export const ShcNodesScreen: Story = {
|
|
544
|
+
render: () => (
|
|
545
|
+
<YStack gap="$4" padding="$4" backgroundColor="$background" data-testid="shc-nodes">
|
|
546
|
+
<XStack alignItems="center" gap="$2">
|
|
547
|
+
<Paragraph flex={1} size="$9" fontWeight="700">
|
|
548
|
+
Nodes
|
|
549
|
+
</Paragraph>
|
|
550
|
+
<Button size="$3" {...shcRoundGlyph} icon={PlusIcon} aria-label="Add node" />
|
|
551
|
+
</XStack>
|
|
552
|
+
<XStack alignItems="center" gap="$2">
|
|
553
|
+
<Paragraph fontFamily="$mono">shc node join</Paragraph>
|
|
554
|
+
<Button size="$2" {...shcRoundGlyph} icon={CopyIcon} aria-label="Copy" />
|
|
555
|
+
</XStack>
|
|
556
|
+
<XStack alignItems="center">
|
|
557
|
+
<Paragraph fontFamily="$mono">shc node join</Paragraph>
|
|
558
|
+
<IconButton inline icon={CopyIcon} aria-label="Copy (inline)" />
|
|
559
|
+
</XStack>
|
|
560
|
+
</YStack>
|
|
561
|
+
),
|
|
562
|
+
};
|
|
563
|
+
|
|
564
|
+
// SHC features/console manage/app/UpdatesPane.tsx as written.
|
|
565
|
+
const shcUpdatePlate = { circular: true, outlined: true } as const;
|
|
566
|
+
|
|
567
|
+
/**
|
|
568
|
+
* Clay's SHC Updates rows (IMG_1926 on FZ-8020): the plate SHC asks for, its
|
|
569
|
+
* glyph now at the circle ratio, above the bare tinted glyph LC-176 wants.
|
|
570
|
+
*/
|
|
571
|
+
export const ShcUpdateRows: Story = {
|
|
572
|
+
render: () => (
|
|
573
|
+
<YStack gap="$2" padding="$4" backgroundColor="$background" data-testid="shc-update-rows">
|
|
574
|
+
{(["$3", "$4"] as const).map((size) => (
|
|
575
|
+
<XStack key={size} alignItems="center" gap="$3" minHeight="$5">
|
|
576
|
+
<YStack flex={1}>
|
|
577
|
+
<Paragraph fontWeight="600">pokemon</Paragraph>
|
|
578
|
+
<Paragraph color="$color11">1.2.0+34</Paragraph>
|
|
579
|
+
</YStack>
|
|
580
|
+
<Button
|
|
581
|
+
size={size}
|
|
582
|
+
{...shcUpdatePlate}
|
|
583
|
+
icon={ArrowCircleUpIcon}
|
|
584
|
+
aria-label={`Update pokemon (plate ${size})`}
|
|
585
|
+
/>
|
|
586
|
+
</XStack>
|
|
587
|
+
))}
|
|
588
|
+
<XStack alignItems="center" gap="$3" minHeight="$5">
|
|
589
|
+
<YStack flex={1}>
|
|
590
|
+
<Paragraph fontWeight="600">pokemon</Paragraph>
|
|
591
|
+
<Paragraph color="$color11">1.2.0+34</Paragraph>
|
|
592
|
+
</YStack>
|
|
593
|
+
<IconButton accentInk icon={ArrowCircleUpIcon} aria-label="Update pokemon" />
|
|
594
|
+
</XStack>
|
|
595
|
+
</YStack>
|
|
596
|
+
),
|
|
597
|
+
};
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @vitest-environment jsdom
|
|
3
|
+
*
|
|
4
|
+
* LC-176 (FZ-8020): icon buttons side by side share one ground, never a row
|
|
5
|
+
* of separate circles. Clay: "they'd actually group together, like you know
|
|
6
|
+
* how water bubbles all kind of group together."
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { cleanup, render, screen } from "@testing-library/react";
|
|
10
|
+
import { createDefaultThemeConfig, Surface } from "@multiplatform.one/theme";
|
|
11
|
+
import type { ReactElement } from "react";
|
|
12
|
+
import { TamaguiProvider } from "tamagui";
|
|
13
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
14
|
+
import { IconButton, IconButtonGroup } from "./index";
|
|
15
|
+
|
|
16
|
+
const glass = vi.hoisted(() => ({ available: false, containerLoads: 0 }));
|
|
17
|
+
vi.mock("./glass", () => ({
|
|
18
|
+
loadGlassView: () =>
|
|
19
|
+
glass.available ? (props: object) => <div data-glass="" {...props} /> : null,
|
|
20
|
+
loadGlassContainer: () => {
|
|
21
|
+
glass.containerLoads += 1;
|
|
22
|
+
return glass.available
|
|
23
|
+
? ({ children, spacing }: { children?: unknown; spacing?: number }) => (
|
|
24
|
+
<div data-glass-container={spacing}>{children as never}</div>
|
|
25
|
+
)
|
|
26
|
+
: null;
|
|
27
|
+
},
|
|
28
|
+
}));
|
|
29
|
+
|
|
30
|
+
afterEach(() => {
|
|
31
|
+
cleanup();
|
|
32
|
+
glass.available = false;
|
|
33
|
+
glass.containerLoads = 0;
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const houseConfig = createDefaultThemeConfig();
|
|
37
|
+
|
|
38
|
+
function Glyph({ size, weight }: { size?: number; weight?: string }) {
|
|
39
|
+
return <svg data-glyph="" data-weight={weight} width={size} height={size} />;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function houseRender(ui: ReactElement) {
|
|
43
|
+
return render(
|
|
44
|
+
<TamaguiProvider config={houseConfig.tamagui} defaultTheme="light" disableInjectCSS>
|
|
45
|
+
{ui}
|
|
46
|
+
</TamaguiProvider>,
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
describe("LC-176: IconButtonGroup", () => {
|
|
51
|
+
it("over media, the group is one capsule and its buttons draw their glyphs alone", () => {
|
|
52
|
+
houseRender(
|
|
53
|
+
<>
|
|
54
|
+
<IconButtonGroup plate="media">
|
|
55
|
+
<IconButton plate="media" icon={Glyph} aria-label="Replace" />
|
|
56
|
+
<IconButton plate="media" icon={Glyph} aria-label="Remove" />
|
|
57
|
+
</IconButtonGroup>
|
|
58
|
+
<IconButton plate="media" icon={Glyph} aria-label="Lone" />
|
|
59
|
+
</>,
|
|
60
|
+
);
|
|
61
|
+
const capsules = document.querySelectorAll('[data-mp-icon-button-group="media"]');
|
|
62
|
+
expect(capsules).toHaveLength(1);
|
|
63
|
+
for (const label of ["Replace", "Remove"]) {
|
|
64
|
+
const button = screen.getByLabelText(label);
|
|
65
|
+
expect(capsules[0]!.contains(button)).toBe(true);
|
|
66
|
+
expect(button.getAttribute("data-mp-icon-button")).not.toBe("media");
|
|
67
|
+
expect(button.querySelector("[data-glyph]")!.getAttribute("data-weight")).toBe("bold");
|
|
68
|
+
}
|
|
69
|
+
// A lone button keeps its own circle.
|
|
70
|
+
expect(screen.getByLabelText("Lone").getAttribute("data-mp-icon-button")).toBe("media");
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it("in a bar with Liquid Glass, the group is one glass capsule and its buttons draw their glyphs alone (FZ-8158)", () => {
|
|
74
|
+
glass.available = true;
|
|
75
|
+
houseRender(
|
|
76
|
+
<Surface bar>
|
|
77
|
+
<IconButtonGroup>
|
|
78
|
+
<IconButton icon={Glyph} aria-label="Archive" />
|
|
79
|
+
<IconButton icon={Glyph} aria-label="Delete" />
|
|
80
|
+
</IconButtonGroup>
|
|
81
|
+
<IconButton icon={Glyph} aria-label="Back" />
|
|
82
|
+
</Surface>,
|
|
83
|
+
);
|
|
84
|
+
const group = document.querySelector('[data-mp-icon-button-group="glass"]')!;
|
|
85
|
+
expect(group).not.toBeNull();
|
|
86
|
+
const capsules = document.querySelectorAll("[data-glass]");
|
|
87
|
+
expect(capsules).toHaveLength(2);
|
|
88
|
+
const capsule = capsules[0]!;
|
|
89
|
+
expect(capsule.parentElement!.contains(group)).toBe(true);
|
|
90
|
+
expect(document.querySelector("[data-glass-container]")).toBeNull();
|
|
91
|
+
for (const label of ["Archive", "Delete"]) {
|
|
92
|
+
const button = screen.getByLabelText(label);
|
|
93
|
+
expect(group.contains(button)).toBe(true);
|
|
94
|
+
expect(button.getAttribute("data-mp-icon-button")).toBe("glyph");
|
|
95
|
+
expect(button.querySelector("[data-glass]")).toBeNull();
|
|
96
|
+
}
|
|
97
|
+
// A lone button keeps its own circle.
|
|
98
|
+
expect(screen.getByLabelText("Back").getAttribute("data-mp-icon-button")).toBe("glass");
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it("without the glass module the bar group is a bare row", () => {
|
|
102
|
+
houseRender(
|
|
103
|
+
<Surface bar>
|
|
104
|
+
<IconButtonGroup>
|
|
105
|
+
<IconButton icon={Glyph} aria-label="Search" />
|
|
106
|
+
<IconButton icon={Glyph} aria-label="More" />
|
|
107
|
+
</IconButtonGroup>
|
|
108
|
+
</Surface>,
|
|
109
|
+
);
|
|
110
|
+
expect(document.querySelector("[data-glass-container]")).toBeNull();
|
|
111
|
+
expect(document.querySelector('[data-mp-icon-button-group="bar"]')).not.toBeNull();
|
|
112
|
+
expect(screen.getByLabelText("Search").getAttribute("data-mp-icon-button")).toBe("glyph");
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it("outside a bar it never loads the glass", () => {
|
|
116
|
+
glass.available = true;
|
|
117
|
+
houseRender(
|
|
118
|
+
<IconButtonGroup>
|
|
119
|
+
<IconButton icon={Glyph} aria-label="Copy" />
|
|
120
|
+
</IconButtonGroup>,
|
|
121
|
+
);
|
|
122
|
+
expect(glass.containerLoads).toBe(0);
|
|
123
|
+
expect(document.querySelector("[data-glass]")).toBeNull();
|
|
124
|
+
expect(document.querySelector('[data-mp-icon-button-group="row"]')).not.toBeNull();
|
|
125
|
+
});
|
|
126
|
+
});
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { radiusClassProps, useBarSurface } from "@multiplatform.one/theme";
|
|
2
|
+
import { useState, type ReactNode } from "react";
|
|
3
|
+
import { isWeb, Theme, View, XStack, type XStackProps } from "tamagui";
|
|
4
|
+
import { loadGlassView } from "./glass";
|
|
5
|
+
import { MEDIA_PLATE_WEB_STYLE } from "./mediaPlate";
|
|
6
|
+
import { IconButtonGroupContext } from "./iconButtonGroupContext";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* LC-176: how far apart two Liquid Glass circles may sit and still merge. Half
|
|
10
|
+
* the 44pt target, so neighbours on the bar's gap always join.
|
|
11
|
+
*/
|
|
12
|
+
export const ICON_BUTTON_GLASS_MERGE_PX = 22;
|
|
13
|
+
|
|
14
|
+
export interface IconButtonGroupProps extends XStackProps {
|
|
15
|
+
/** Over imagery or video: the group shares one material capsule. */
|
|
16
|
+
plate?: "media";
|
|
17
|
+
children?: ReactNode;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* LC-176: icon buttons that sit side by side. Grounds merge, they never stand
|
|
22
|
+
* as a row of separate circles: on iOS 26 the bar's group is one Liquid Glass
|
|
23
|
+
* capsule, as the system draws a bar's item group (merging each circle through
|
|
24
|
+
* the glass container left a waist between them, FZ-8158); over media the
|
|
25
|
+
* group shares one material capsule; a bare row stays bare. A lone button
|
|
26
|
+
* keeps its own circle.
|
|
27
|
+
*/
|
|
28
|
+
export function IconButtonGroup({ plate, children, ...rest }: IconButtonGroupProps) {
|
|
29
|
+
const bar = useBarSurface();
|
|
30
|
+
const [capsuleHeight, setCapsuleHeight] = useState(0);
|
|
31
|
+
if (plate === "media") {
|
|
32
|
+
return (
|
|
33
|
+
<IconButtonGroupContext.Provider value="media">
|
|
34
|
+
<Theme name="dark">
|
|
35
|
+
<XStack
|
|
36
|
+
alignItems="center"
|
|
37
|
+
alignSelf="flex-start"
|
|
38
|
+
borderRadius={100000}
|
|
39
|
+
backgroundColor="$shadow4"
|
|
40
|
+
overflow="hidden"
|
|
41
|
+
data-mp-icon-button-group="media"
|
|
42
|
+
{...radiusClassProps("R-IDENTITY", "icon button plate")}
|
|
43
|
+
{...(isWeb ? { style: MEDIA_PLATE_WEB_STYLE } : undefined)}
|
|
44
|
+
{...rest}
|
|
45
|
+
>
|
|
46
|
+
{children}
|
|
47
|
+
</XStack>
|
|
48
|
+
</Theme>
|
|
49
|
+
</IconButtonGroupContext.Provider>
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
const GlassView = bar ? loadGlassView() : null;
|
|
53
|
+
const row = (
|
|
54
|
+
<XStack
|
|
55
|
+
alignItems="center"
|
|
56
|
+
data-mp-icon-button-group={GlassView ? "glass" : bar ? "bar" : "row"}
|
|
57
|
+
{...rest}
|
|
58
|
+
>
|
|
59
|
+
{children}
|
|
60
|
+
</XStack>
|
|
61
|
+
);
|
|
62
|
+
if (!GlassView) return row;
|
|
63
|
+
return (
|
|
64
|
+
<IconButtonGroupContext.Provider value="glass">
|
|
65
|
+
<View
|
|
66
|
+
position="relative"
|
|
67
|
+
flexShrink={1}
|
|
68
|
+
minWidth={0}
|
|
69
|
+
onLayout={(event) => setCapsuleHeight(event.nativeEvent.layout.height)}
|
|
70
|
+
>
|
|
71
|
+
<GlassView
|
|
72
|
+
glassEffectStyle="regular"
|
|
73
|
+
pointerEvents="none"
|
|
74
|
+
style={{
|
|
75
|
+
position: "absolute",
|
|
76
|
+
top: 0,
|
|
77
|
+
right: 0,
|
|
78
|
+
bottom: 0,
|
|
79
|
+
left: 0,
|
|
80
|
+
borderRadius: capsuleHeight / 2,
|
|
81
|
+
}}
|
|
82
|
+
/>
|
|
83
|
+
{row}
|
|
84
|
+
</View>
|
|
85
|
+
</IconButtonGroupContext.Provider>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { GlassContainerComponent, GlassViewComponent } from "./glass";
|
|
2
|
+
|
|
3
|
+
export type { GlassContainerComponent, GlassViewComponent } from "./glass";
|
|
4
|
+
|
|
5
|
+
let cachedGlassView: GlassViewComponent | null | undefined;
|
|
6
|
+
let cachedGlassContainer: GlassContainerComponent | null | undefined;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* LC-176: expo-glass-effect's `GlassView`, when this binary links its native
|
|
10
|
+
* module and the OS draws Liquid Glass (iOS 26+); Android never links it. The module is probed through
|
|
11
|
+
* `requireOptionalNativeModule` BEFORE the guarded `require`, so an OTA on a
|
|
12
|
+
* binary built without it, or an older iOS, falls back to the bare tinted
|
|
13
|
+
* glyph instead of throwing mid-render (the `nativePickerLoader.ts` contract).
|
|
14
|
+
*/
|
|
15
|
+
export function loadGlassView(): GlassViewComponent | null {
|
|
16
|
+
if (cachedGlassView !== undefined) return cachedGlassView;
|
|
17
|
+
cachedGlassView = null;
|
|
18
|
+
try {
|
|
19
|
+
const core = require("expo-modules-core") as {
|
|
20
|
+
requireOptionalNativeModule?: (moduleName: string) => unknown;
|
|
21
|
+
};
|
|
22
|
+
if (core.requireOptionalNativeModule?.("ExpoGlassEffect") == null) return cachedGlassView;
|
|
23
|
+
const glass = require("expo-glass-effect") as {
|
|
24
|
+
GlassView?: GlassViewComponent;
|
|
25
|
+
GlassContainer?: GlassContainerComponent;
|
|
26
|
+
isLiquidGlassAvailable?: () => boolean;
|
|
27
|
+
isGlassEffectAPIAvailable?: () => boolean;
|
|
28
|
+
};
|
|
29
|
+
if (
|
|
30
|
+
glass.GlassView &&
|
|
31
|
+
glass.isLiquidGlassAvailable?.() &&
|
|
32
|
+
glass.isGlassEffectAPIAvailable?.()
|
|
33
|
+
) {
|
|
34
|
+
cachedGlassView = glass.GlassView;
|
|
35
|
+
cachedGlassContainer = glass.GlassContainer ?? null;
|
|
36
|
+
}
|
|
37
|
+
} catch {
|
|
38
|
+
cachedGlassView = null;
|
|
39
|
+
}
|
|
40
|
+
return cachedGlassView;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* LC-176: expo-glass-effect's `GlassContainer`, present exactly when
|
|
45
|
+
* `loadGlassView` found Liquid Glass. A binary whose module predates the
|
|
46
|
+
* container keeps separate circles rather than throwing.
|
|
47
|
+
*/
|
|
48
|
+
export function loadGlassContainer(): GlassContainerComponent | null {
|
|
49
|
+
if (cachedGlassView === undefined) loadGlassView();
|
|
50
|
+
return cachedGlassContainer ?? null;
|
|
51
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { ComponentType, ReactNode } from "react";
|
|
2
|
+
|
|
3
|
+
/** The slice of expo-glass-effect's `GlassView` an icon button draws. */
|
|
4
|
+
export type GlassViewComponent = ComponentType<{
|
|
5
|
+
style?: unknown;
|
|
6
|
+
glassEffectStyle?: "regular" | "clear";
|
|
7
|
+
isInteractive?: boolean;
|
|
8
|
+
pointerEvents?: "none" | "auto" | "box-none" | "box-only";
|
|
9
|
+
}>;
|
|
10
|
+
|
|
11
|
+
/** The slice of expo-glass-effect's `GlassContainer` an icon button group draws. */
|
|
12
|
+
export type GlassContainerComponent = ComponentType<{
|
|
13
|
+
spacing?: number;
|
|
14
|
+
style?: unknown;
|
|
15
|
+
children?: ReactNode;
|
|
16
|
+
}>;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* LC-176: the iOS 26 Liquid Glass material an icon button floats on in a bar.
|
|
20
|
+
* Only iOS has it (`glass.native.ts`; the native module is Apple-only, so Android probes null); web, Android and GNOME draw the bare
|
|
21
|
+
* tinted glyph.
|
|
22
|
+
*/
|
|
23
|
+
export function loadGlassView(): GlassViewComponent | null {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* LC-176: the container that merges neighbouring Liquid Glass circles into one
|
|
29
|
+
* shape, as iOS 26 groups bar buttons. Only iOS has it.
|
|
30
|
+
*/
|
|
31
|
+
export function loadGlassContainer(): GlassContainerComponent | null {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { createContext, useContext } from "react";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* LC-176: the ground an `IconButtonGroup` paints under its buttons. `media`
|
|
5
|
+
* is one shared material capsule and `glass` one shared Liquid Glass capsule,
|
|
6
|
+
* so a button inside draws its bare glyph instead of its own circle.
|
|
7
|
+
*/
|
|
8
|
+
export type IconButtonGroupGround = "media" | "glass" | null;
|
|
9
|
+
|
|
10
|
+
export const IconButtonGroupContext = createContext<IconButtonGroupGround>(null);
|
|
11
|
+
|
|
12
|
+
export function useIconButtonGroupGround(): IconButtonGroupGround {
|
|
13
|
+
return useContext(IconButtonGroupContext);
|
|
14
|
+
}
|