@hzblj/kara-components 0.6.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +91 -0
- package/README.md +31 -3
- package/lib/module/atoms/activity-indicator/activity-indicator.android.js +42 -0
- package/lib/module/atoms/activity-indicator/activity-indicator.android.js.map +1 -0
- package/lib/module/atoms/activity-indicator/activity-indicator.ios.js +19 -0
- package/lib/module/atoms/activity-indicator/activity-indicator.ios.js.map +1 -0
- package/lib/module/atoms/activity-indicator/activity-indicator.types.js +4 -0
- package/lib/module/atoms/activity-indicator/activity-indicator.types.js.map +1 -0
- package/lib/module/atoms/activity-indicator/index.js +2 -1
- package/lib/module/atoms/activity-indicator/index.js.map +1 -1
- package/lib/module/atoms/avatar/avatar.js +2 -4
- package/lib/module/atoms/avatar/avatar.js.map +1 -1
- package/lib/module/atoms/blur-view/blur-view.android.js +100 -0
- package/lib/module/atoms/blur-view/blur-view.android.js.map +1 -0
- package/lib/module/atoms/blur-view/blur-view.ios.js +43 -0
- package/lib/module/atoms/blur-view/blur-view.ios.js.map +1 -0
- package/lib/module/atoms/blur-view/blur-view.types.js +4 -0
- package/lib/module/atoms/blur-view/blur-view.types.js.map +1 -0
- package/lib/module/atoms/blur-view/index.js +5 -0
- package/lib/module/atoms/blur-view/index.js.map +1 -0
- package/lib/module/atoms/checkbox/checkbox.android.js +7 -3
- package/lib/module/atoms/checkbox/checkbox.android.js.map +1 -1
- package/lib/module/atoms/date-picker/date-picker.ios.js +32 -11
- package/lib/module/atoms/date-picker/date-picker.ios.js.map +1 -1
- package/lib/module/atoms/glass-view/glass-view.android.js +36 -5
- package/lib/module/atoms/glass-view/glass-view.android.js.map +1 -1
- package/lib/module/atoms/image/image.js +13 -6
- package/lib/module/atoms/image/image.js.map +1 -1
- package/lib/module/atoms/image/image.types.js +4 -0
- package/lib/module/atoms/image/image.types.js.map +1 -0
- package/lib/module/atoms/image/index.js +1 -0
- package/lib/module/atoms/image/index.js.map +1 -1
- package/lib/module/atoms/index.js +1 -1
- package/lib/module/atoms/index.js.map +1 -1
- package/lib/module/atoms/island/island.android.js +6 -2
- package/lib/module/atoms/island/island.android.js.map +1 -1
- package/lib/module/atoms/island/island.ios.js +5 -2
- package/lib/module/atoms/island/island.ios.js.map +1 -1
- package/lib/module/atoms/pressable/pressable.js +38 -6
- package/lib/module/atoms/pressable/pressable.js.map +1 -1
- package/lib/module/atoms/rn-flat-list/rn-flat-list.js +10 -9
- package/lib/module/atoms/rn-flat-list/rn-flat-list.js.map +1 -1
- package/lib/module/atoms/rn-section-list/rn-section-list.js +5 -8
- package/lib/module/atoms/rn-section-list/rn-section-list.js.map +1 -1
- package/lib/module/atoms/rn-text/rn-text.js +5 -2
- package/lib/module/atoms/rn-text/rn-text.js.map +1 -1
- package/lib/module/atoms/segmented-control/index.js +1 -1
- package/lib/module/atoms/segmented-control/index.js.map +1 -1
- package/lib/module/atoms/segmented-control/segmented-control.android.js +74 -0
- package/lib/module/atoms/segmented-control/segmented-control.android.js.map +1 -0
- package/lib/module/atoms/segmented-control/segmented-control.ios.js +44 -0
- package/lib/module/atoms/segmented-control/segmented-control.ios.js.map +1 -0
- package/lib/module/atoms/skeleton/skeleton.js +5 -5
- package/lib/module/atoms/skeleton/skeleton.js.map +1 -1
- package/lib/module/atoms/switch/switch.android.js +4 -1
- package/lib/module/atoms/switch/switch.android.js.map +1 -1
- package/lib/module/atoms/text/text.android.js +5 -2
- package/lib/module/atoms/text/text.android.js.map +1 -1
- package/lib/module/atoms/text/text.ios.js +14 -3
- package/lib/module/atoms/text/text.ios.js.map +1 -1
- package/lib/module/molecules/collapsible/collapsible.android.js +37 -7
- package/lib/module/molecules/collapsible/collapsible.android.js.map +1 -1
- package/lib/module/molecules/navigation-bar/navigation-bar.android.js +2 -8
- package/lib/module/molecules/navigation-bar/navigation-bar.android.js.map +1 -1
- package/lib/module/molecules/pager/pager.android.js +5 -4
- package/lib/module/molecules/pager/pager.android.js.map +1 -1
- package/lib/module/molecules/scroll-edge-container/scroll-edge-container.android.js +114 -11
- package/lib/module/molecules/scroll-edge-container/scroll-edge-container.android.js.map +1 -1
- package/lib/module/molecules/search-bar/search-bar.js +15 -2
- package/lib/module/molecules/search-bar/search-bar.js.map +1 -1
- package/lib/module/molecules/stepper/stepper.ios.js +45 -8
- package/lib/module/molecules/stepper/stepper.ios.js.map +1 -1
- package/lib/module/molecules/tooltip/tooltip.android.js +16 -5
- package/lib/module/molecules/tooltip/tooltip.android.js.map +1 -1
- package/lib/module/organisms/action-sheet/action-sheet.android.js +41 -20
- package/lib/module/organisms/action-sheet/action-sheet.android.js.map +1 -1
- package/lib/module/organisms/action-sheet/action-sheet.ios.js +24 -8
- package/lib/module/organisms/action-sheet/action-sheet.ios.js.map +1 -1
- package/lib/module/organisms/alert/alert.ios.js +15 -2
- package/lib/module/organisms/alert/alert.ios.js.map +1 -1
- package/lib/module/organisms/form/form.android.js +22 -9
- package/lib/module/organisms/form/form.android.js.map +1 -1
- package/lib/module/organisms/swipe-actions/swipe-actions.android.js +125 -11
- package/lib/module/organisms/swipe-actions/swipe-actions.android.js.map +1 -1
- package/lib/module/organisms/swipe-actions/swipe-actions.ios.js +46 -18
- package/lib/module/organisms/swipe-actions/swipe-actions.ios.js.map +1 -1
- package/lib/module/ripple-clip.js +9 -1
- package/lib/module/ripple-clip.js.map +1 -1
- package/lib/module/templates/screen/screen.android.js +31 -10
- package/lib/module/templates/screen/screen.android.js.map +1 -1
- package/lib/typescript/atoms/activity-indicator/activity-indicator.d.ts +1 -3
- package/lib/typescript/atoms/activity-indicator/activity-indicator.ios.d.ts +3 -0
- package/lib/typescript/atoms/activity-indicator/activity-indicator.types.d.ts +17 -0
- package/lib/typescript/atoms/activity-indicator/index.d.ts +1 -0
- package/lib/typescript/atoms/avatar/avatar.types.d.ts +5 -0
- package/lib/typescript/atoms/badge/badge.types.d.ts +3 -0
- package/lib/typescript/atoms/blur-view/blur-view.d.ts +1 -0
- package/lib/typescript/atoms/blur-view/blur-view.ios.d.ts +3 -0
- package/lib/typescript/atoms/blur-view/blur-view.types.d.ts +31 -0
- package/lib/typescript/atoms/blur-view/index.d.ts +2 -0
- package/lib/typescript/atoms/box/a-box.d.ts +1 -1
- package/lib/typescript/atoms/button/button.types.d.ts +12 -0
- package/lib/typescript/atoms/card/card.types.d.ts +6 -0
- package/lib/typescript/atoms/checkbox/checkbox.types.d.ts +5 -0
- package/lib/typescript/atoms/chip/chip.types.d.ts +5 -0
- package/lib/typescript/atoms/color-picker/color-picker.types.d.ts +4 -0
- package/lib/typescript/atoms/date-picker/date-picker.ios.d.ts +6 -1
- package/lib/typescript/atoms/date-picker/date-picker.types.d.ts +14 -0
- package/lib/typescript/atoms/divider/divider.types.d.ts +3 -0
- package/lib/typescript/atoms/fab/fab.types.d.ts +7 -0
- package/lib/typescript/atoms/glass-view/glass-view.types.d.ts +13 -5
- package/lib/typescript/atoms/h-stack/h-stack.types.d.ts +6 -0
- package/lib/typescript/atoms/icon/icon.types.d.ts +8 -0
- package/lib/typescript/atoms/icon-button/icon-button.types.d.ts +5 -0
- package/lib/typescript/atoms/image/image.d.ts +12 -4
- package/lib/typescript/atoms/image/image.types.d.ts +4 -0
- package/lib/typescript/atoms/image/index.d.ts +1 -0
- package/lib/typescript/atoms/index.d.ts +1 -1
- package/lib/typescript/atoms/island/island.types.d.ts +10 -2
- package/lib/typescript/atoms/list/list.types.d.ts +2 -0
- package/lib/typescript/atoms/picker/picker.types.d.ts +12 -0
- package/lib/typescript/atoms/pressable/pressable.types.d.ts +10 -0
- package/lib/typescript/atoms/progress/progress.types.d.ts +6 -0
- package/lib/typescript/atoms/rn-flat-list/rn-flat-list.d.ts +5 -8
- package/lib/typescript/atoms/rn-flat-list/rn-flat-list.types.d.ts +3 -1
- package/lib/typescript/atoms/rn-scroll-view/rn-scroll-view.types.d.ts +4 -0
- package/lib/typescript/atoms/rn-section-list/rn-section-list.d.ts +2 -8
- package/lib/typescript/atoms/rn-section-list/rn-section-list.types.d.ts +3 -1
- package/lib/typescript/atoms/rn-text/rn-text.types.d.ts +10 -1
- package/lib/typescript/atoms/rn-view/rn-view.types.d.ts +2 -0
- package/lib/typescript/atoms/scroll-view/scroll-view.types.d.ts +4 -0
- package/lib/typescript/atoms/segmented-control/segmented-control.d.ts +1 -6
- package/lib/typescript/atoms/segmented-control/segmented-control.ios.d.ts +7 -0
- package/lib/typescript/atoms/segmented-control/segmented-control.types.d.ts +13 -0
- package/lib/typescript/atoms/skeleton/skeleton.types.d.ts +11 -2
- package/lib/typescript/atoms/slider/slider.types.d.ts +7 -0
- package/lib/typescript/atoms/spacer/spacer.types.d.ts +3 -0
- package/lib/typescript/atoms/surface/surface.types.d.ts +7 -0
- package/lib/typescript/atoms/switch/switch.types.d.ts +8 -0
- package/lib/typescript/atoms/text/text.types.d.ts +18 -1
- package/lib/typescript/atoms/text-field/text-field.types.d.ts +12 -0
- package/lib/typescript/atoms/v-stack/v-stack.types.d.ts +9 -0
- package/lib/typescript/atoms/z-stack/z-stack.types.d.ts +8 -0
- package/lib/typescript/molecules/collapsible/collapsible.types.d.ts +8 -0
- package/lib/typescript/molecules/empty-state/empty-state.types.d.ts +5 -0
- package/lib/typescript/molecules/label/label.types.d.ts +6 -0
- package/lib/typescript/molecules/labeled-content/labeled-content.types.d.ts +6 -0
- package/lib/typescript/molecules/link/link.types.d.ts +6 -0
- package/lib/typescript/molecules/list-item/list-item.types.d.ts +9 -0
- package/lib/typescript/molecules/navigation-bar/navigation-bar.types.d.ts +8 -0
- package/lib/typescript/molecules/pager/pager.types.d.ts +2 -0
- package/lib/typescript/molecules/radio-group/radio-group.types.d.ts +10 -0
- package/lib/typescript/molecules/rn-list-item/rn-list-item.types.d.ts +1 -1
- package/lib/typescript/molecules/rn-tooltip/rn-tooltip.types.d.ts +11 -0
- package/lib/typescript/molecules/scroll-edge-container/scroll-edge-container.types.d.ts +7 -2
- package/lib/typescript/molecules/search-bar/search-bar.types.d.ts +8 -0
- package/lib/typescript/molecules/stepper/stepper.ios.d.ts +5 -0
- package/lib/typescript/molecules/stepper/stepper.types.d.ts +10 -0
- package/lib/typescript/molecules/tooltip/tooltip.types.d.ts +12 -0
- package/lib/typescript/organisms/action-sheet/action-sheet.types.d.ts +8 -0
- package/lib/typescript/organisms/alert/alert.types.d.ts +11 -0
- package/lib/typescript/organisms/context-menu/context-menu.types.d.ts +4 -0
- package/lib/typescript/organisms/fab-menu/fab-menu.types.d.ts +2 -0
- package/lib/typescript/organisms/form/form.types.d.ts +6 -0
- package/lib/typescript/organisms/menu/menu.types.d.ts +17 -0
- package/lib/typescript/organisms/sheet/sheet.types.d.ts +19 -0
- package/lib/typescript/organisms/snackbar/snackbar.types.d.ts +8 -0
- package/lib/typescript/organisms/swipe-actions/swipe-actions.types.d.ts +36 -13
- package/lib/typescript/templates/screen/screen.types.d.ts +9 -0
- package/package.json +5 -7
- package/src/atoms/activity-indicator/activity-indicator.android.tsx +32 -0
- package/src/atoms/activity-indicator/activity-indicator.ios.tsx +15 -0
- package/src/atoms/activity-indicator/activity-indicator.types.ts +20 -0
- package/src/atoms/activity-indicator/index.ts +1 -0
- package/src/atoms/avatar/avatar.tsx +1 -1
- package/src/atoms/avatar/avatar.types.ts +5 -0
- package/src/atoms/badge/badge.types.ts +3 -0
- package/src/atoms/blur-view/blur-view.android.tsx +68 -0
- package/src/atoms/blur-view/blur-view.ios.tsx +37 -0
- package/src/atoms/blur-view/blur-view.types.ts +34 -0
- package/src/atoms/blur-view/index.ts +2 -0
- package/src/atoms/button/button.types.ts +12 -0
- package/src/atoms/card/card.types.ts +6 -0
- package/src/atoms/checkbox/checkbox.android.tsx +7 -3
- package/src/atoms/checkbox/checkbox.types.ts +5 -0
- package/src/atoms/chip/chip.types.ts +5 -0
- package/src/atoms/color-picker/color-picker.types.ts +4 -0
- package/src/atoms/date-picker/date-picker.ios.tsx +33 -13
- package/src/atoms/date-picker/date-picker.types.ts +14 -0
- package/src/atoms/divider/divider.types.ts +3 -0
- package/src/atoms/fab/fab.types.ts +7 -0
- package/src/atoms/glass-view/glass-view.android.tsx +41 -3
- package/src/atoms/glass-view/glass-view.types.ts +13 -5
- package/src/atoms/h-stack/h-stack.types.ts +6 -0
- package/src/atoms/icon/icon.types.ts +8 -0
- package/src/atoms/icon-button/icon-button.types.ts +5 -0
- package/src/atoms/image/image.tsx +11 -12
- package/src/atoms/image/image.types.ts +6 -0
- package/src/atoms/image/index.ts +1 -0
- package/src/atoms/index.ts +1 -1
- package/src/atoms/island/island.android.tsx +10 -2
- package/src/atoms/island/island.ios.tsx +4 -2
- package/src/atoms/island/island.types.ts +10 -2
- package/src/atoms/list/list.types.ts +2 -0
- package/src/atoms/picker/picker.types.ts +12 -0
- package/src/atoms/pressable/pressable.tsx +46 -6
- package/src/atoms/pressable/pressable.types.ts +10 -0
- package/src/atoms/progress/progress.types.ts +6 -0
- package/src/atoms/rn-flat-list/rn-flat-list.tsx +8 -8
- package/src/atoms/rn-flat-list/rn-flat-list.types.ts +3 -1
- package/src/atoms/rn-scroll-view/rn-scroll-view.types.ts +4 -0
- package/src/atoms/rn-section-list/rn-section-list.tsx +5 -8
- package/src/atoms/rn-section-list/rn-section-list.types.ts +3 -1
- package/src/atoms/rn-text/rn-text.tsx +6 -3
- package/src/atoms/rn-text/rn-text.types.ts +15 -5
- package/src/atoms/rn-view/rn-view.types.ts +2 -0
- package/src/atoms/scroll-view/scroll-view.types.ts +4 -0
- package/src/atoms/segmented-control/segmented-control.android.tsx +79 -0
- package/src/atoms/segmented-control/segmented-control.ios.tsx +47 -0
- package/src/atoms/segmented-control/segmented-control.types.ts +13 -0
- package/src/atoms/skeleton/skeleton.tsx +4 -4
- package/src/atoms/skeleton/skeleton.types.ts +11 -2
- package/src/atoms/slider/slider.types.ts +7 -0
- package/src/atoms/spacer/spacer.types.ts +3 -0
- package/src/atoms/surface/surface.types.ts +7 -0
- package/src/atoms/switch/switch.android.tsx +4 -1
- package/src/atoms/switch/switch.types.ts +8 -0
- package/src/atoms/text/text.android.tsx +7 -1
- package/src/atoms/text/text.ios.tsx +14 -3
- package/src/atoms/text/text.types.ts +18 -1
- package/src/atoms/text-field/text-field.types.ts +12 -0
- package/src/atoms/v-stack/v-stack.types.ts +9 -0
- package/src/atoms/z-stack/z-stack.types.ts +8 -0
- package/src/molecules/collapsible/collapsible.android.tsx +47 -7
- package/src/molecules/collapsible/collapsible.types.ts +8 -0
- package/src/molecules/empty-state/empty-state.types.ts +5 -0
- package/src/molecules/label/label.types.ts +6 -0
- package/src/molecules/labeled-content/labeled-content.types.ts +6 -0
- package/src/molecules/link/link.types.ts +6 -0
- package/src/molecules/list-item/list-item.types.ts +9 -0
- package/src/molecules/navigation-bar/navigation-bar.android.tsx +1 -6
- package/src/molecules/navigation-bar/navigation-bar.types.ts +8 -0
- package/src/molecules/pager/pager.android.tsx +4 -1
- package/src/molecules/pager/pager.types.ts +2 -0
- package/src/molecules/radio-group/radio-group.types.ts +10 -0
- package/src/molecules/rn-list-item/rn-list-item.types.ts +1 -1
- package/src/molecules/rn-tooltip/rn-tooltip.types.ts +11 -0
- package/src/molecules/scroll-edge-container/scroll-edge-container.android.tsx +103 -10
- package/src/molecules/scroll-edge-container/scroll-edge-container.types.ts +7 -2
- package/src/molecules/search-bar/search-bar.tsx +9 -2
- package/src/molecules/search-bar/search-bar.types.ts +8 -0
- package/src/molecules/stepper/stepper.ios.tsx +37 -1
- package/src/molecules/stepper/stepper.types.ts +10 -0
- package/src/molecules/tooltip/tooltip.android.tsx +17 -9
- package/src/molecules/tooltip/tooltip.types.ts +12 -0
- package/src/organisms/action-sheet/action-sheet.android.tsx +37 -19
- package/src/organisms/action-sheet/action-sheet.ios.tsx +22 -9
- package/src/organisms/action-sheet/action-sheet.types.ts +8 -0
- package/src/organisms/alert/alert.ios.tsx +11 -1
- package/src/organisms/alert/alert.types.ts +11 -0
- package/src/organisms/context-menu/context-menu.types.ts +4 -0
- package/src/organisms/fab-menu/fab-menu.types.ts +2 -0
- package/src/organisms/form/form.android.tsx +15 -7
- package/src/organisms/form/form.types.ts +6 -0
- package/src/organisms/menu/menu.types.ts +17 -0
- package/src/organisms/sheet/sheet.types.ts +19 -0
- package/src/organisms/snackbar/snackbar.types.ts +8 -0
- package/src/organisms/swipe-actions/swipe-actions.android.tsx +129 -9
- package/src/organisms/swipe-actions/swipe-actions.ios.tsx +45 -11
- package/src/organisms/swipe-actions/swipe-actions.types.ts +37 -14
- package/src/ripple-clip.ts +10 -1
- package/src/templates/screen/screen.android.tsx +33 -12
- package/src/templates/screen/screen.types.ts +9 -0
- package/lib/module/atoms/activity-indicator/activity-indicator.js +0 -14
- package/lib/module/atoms/activity-indicator/activity-indicator.js.map +0 -1
- package/lib/module/atoms/flash-list/flash-list.js +0 -31
- package/lib/module/atoms/flash-list/flash-list.js.map +0 -1
- package/lib/module/atoms/flash-list/flash-list.types.js +0 -4
- package/lib/module/atoms/flash-list/flash-list.types.js.map +0 -1
- package/lib/module/atoms/flash-list/index.js +0 -5
- package/lib/module/atoms/flash-list/index.js.map +0 -1
- package/lib/module/atoms/segmented-control/segmented-control.js +0 -40
- package/lib/module/atoms/segmented-control/segmented-control.js.map +0 -1
- package/lib/typescript/atoms/flash-list/flash-list.d.ts +0 -8
- package/lib/typescript/atoms/flash-list/flash-list.types.d.ts +0 -18
- package/lib/typescript/atoms/flash-list/index.d.ts +0 -2
- package/src/atoms/activity-indicator/activity-indicator.tsx +0 -9
- package/src/atoms/flash-list/flash-list.tsx +0 -28
- package/src/atoms/flash-list/flash-list.types.ts +0 -20
- package/src/atoms/flash-list/index.ts +0 -2
- package/src/atoms/segmented-control/segmented-control.tsx +0 -47
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
2
|
import type { MenuAction } from '../menu';
|
|
3
|
+
/**
|
|
4
|
+
* A list of actions to pick one from: SwiftUI's confirmation dialog on iOS, a `Sheet` of Material rows — icon and label
|
|
5
|
+
* — under the title on Android. A native atom — it renders inside `<Screen>` or `<Island>`.
|
|
6
|
+
*/
|
|
3
7
|
export type ActionSheetProps = {
|
|
8
|
+
/** The choices, top to bottom. Picking one closes the sheet. */
|
|
4
9
|
actions: MenuAction[];
|
|
10
|
+
/** The sheet closed — after a pick too — so turn `visible` off. */
|
|
5
11
|
onDismiss: () => void;
|
|
12
|
+
/** The heading above the actions. Android always shows it; on iOS the system decides, and may hide it. */
|
|
6
13
|
title: string;
|
|
14
|
+
/** Presents the sheet while true. */
|
|
7
15
|
visible: boolean;
|
|
8
16
|
};
|
|
9
17
|
export type ActionSheetComponent = (props: ActionSheetProps) => ReactNode;
|
|
@@ -1,11 +1,22 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* A modal alert with a confirm button and Cancel: SwiftUI's alert on iOS, Material's `AlertDialog` on Android. A native
|
|
4
|
+
* atom — it renders inside `<Screen>` or `<Island>`.
|
|
5
|
+
*/
|
|
2
6
|
export type AlertProps = {
|
|
7
|
+
/** The confirm button's label. `OK` by default. */
|
|
3
8
|
confirmLabel?: string;
|
|
9
|
+
/** Marks the confirm button destructive — the system's destructive role on iOS, the destructive colour on Android. */
|
|
4
10
|
destructive?: boolean;
|
|
11
|
+
/** The body text, under the title. */
|
|
5
12
|
message?: string;
|
|
13
|
+
/** The confirm button was pressed. `onDismiss` follows, as it does for every way the alert closes. */
|
|
6
14
|
onConfirm?: () => void;
|
|
15
|
+
/** The alert closed — either button, or on Android a tap outside or back — so turn `visible` off. */
|
|
7
16
|
onDismiss: () => void;
|
|
17
|
+
/** The alert's heading. */
|
|
8
18
|
title: string;
|
|
19
|
+
/** Shows the alert while true. */
|
|
9
20
|
visible: boolean;
|
|
10
21
|
};
|
|
11
22
|
export type AlertComponent = (props: AlertProps) => ReactNode;
|
|
@@ -9,6 +9,7 @@ import type { MenuAction } from '../menu';
|
|
|
9
9
|
* needs no `Screen` or `Island`.
|
|
10
10
|
*/
|
|
11
11
|
export type ContextMenuProps = ResponsiveViewSx & {
|
|
12
|
+
/** The menu's actions, top to bottom. */
|
|
12
13
|
actions: MenuAction[];
|
|
13
14
|
/** The trigger, laid out as usual. */
|
|
14
15
|
children: ReactNode;
|
|
@@ -28,9 +29,12 @@ export type ContextMenuProps = ResponsiveViewSx & {
|
|
|
28
29
|
* `ZoomTransition.Source`, `pop` otherwise.
|
|
29
30
|
*/
|
|
30
31
|
previewTransition?: 'dismiss' | 'pop' | 'zoom';
|
|
32
|
+
/** Test identifier — on the native view around the trigger on iOS, on the trigger's `Pressable` on Android. */
|
|
31
33
|
testID?: string;
|
|
32
34
|
};
|
|
35
|
+
/** A region of a `ContextMenu`'s trigger that keeps its own long press — see `ContextMenu.Exclusion`. */
|
|
33
36
|
export type ContextMenuExclusionProps = {
|
|
37
|
+
/** The region — a button inside the card, say. */
|
|
34
38
|
children: ReactNode;
|
|
35
39
|
};
|
|
36
40
|
export type ContextMenuComponent = ((props: ContextMenuProps) => ReactNode) & {
|
|
@@ -7,9 +7,11 @@ import type { MenuAction } from '../menu';
|
|
|
7
7
|
* of a SwiftUI `Menu`. A native atom — it renders inside `<Screen>` or `<Island>`.
|
|
8
8
|
*/
|
|
9
9
|
export type FabMenuProps = {
|
|
10
|
+
/** The actions the button unfolds into. */
|
|
10
11
|
actions: MenuAction[];
|
|
11
12
|
/** The closed button's icon — one of the app's. A plus by default. */
|
|
12
13
|
icon?: IconName;
|
|
14
|
+
/** Test identifier, forwarded to the SwiftUI `Menu`. iOS only — Android ignores it. */
|
|
13
15
|
testID?: string;
|
|
14
16
|
};
|
|
15
17
|
export type FabMenuComponent = (props: FabMenuProps) => ReactNode;
|
|
@@ -5,12 +5,18 @@ import type { ReactNode } from 'react';
|
|
|
5
5
|
* an island that sizes itself to its content it has no height and shows nothing.
|
|
6
6
|
*/
|
|
7
7
|
export type FormProps = {
|
|
8
|
+
/** The form's `Form.Section`s. */
|
|
8
9
|
children: ReactNode;
|
|
10
|
+
/** Test identifier, forwarded to the native view. */
|
|
9
11
|
testID?: string;
|
|
10
12
|
};
|
|
13
|
+
/** A group of rows in a `Form`: a SwiftUI `Section` on iOS, a `Card` under a heading on Android. */
|
|
11
14
|
export type FormSectionProps = {
|
|
15
|
+
/** The section's rows. */
|
|
12
16
|
children: ReactNode;
|
|
17
|
+
/** Small print under the section. */
|
|
13
18
|
footer?: string;
|
|
19
|
+
/** The section's heading. */
|
|
14
20
|
title?: string;
|
|
15
21
|
};
|
|
16
22
|
export type FormComponent = ((props: FormProps) => ReactNode) & {
|
|
@@ -1,13 +1,30 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
2
|
import type { IconName } from '../../atoms/icon';
|
|
3
|
+
/** One action of a `Menu`, an `ActionSheet`, a `ContextMenu` or a `FabMenu`. */
|
|
3
4
|
export type MenuAction = {
|
|
5
|
+
/**
|
|
6
|
+
* Draws the action in the destructive colour — the system's destructive role on iOS. `FabMenu` ignores it on
|
|
7
|
+
* Android.
|
|
8
|
+
*/
|
|
4
9
|
destructive?: boolean;
|
|
10
|
+
/**
|
|
11
|
+
* One of the app's icons, before the label. `ContextMenu` and `FabMenu` show it on both platforms, `Menu` and
|
|
12
|
+
* `ActionSheet` on Android only.
|
|
13
|
+
*/
|
|
5
14
|
icon?: IconName;
|
|
15
|
+
/** The action's text. Rows are keyed by it, so keep labels unique within one list. */
|
|
6
16
|
label: string;
|
|
17
|
+
/** Called when the action is chosen; the menu closes by itself. */
|
|
7
18
|
onPress: () => void;
|
|
8
19
|
};
|
|
20
|
+
/**
|
|
21
|
+
* A text button that opens a list of actions: a SwiftUI `Menu` on iOS, a Material dropdown anchored to it on Android.
|
|
22
|
+
* A native atom — it renders inside `<Screen>` or `<Island>`.
|
|
23
|
+
*/
|
|
9
24
|
export type MenuProps = {
|
|
25
|
+
/** The menu's actions, top to bottom. */
|
|
10
26
|
actions: MenuAction[];
|
|
27
|
+
/** The trigger's text. Android draws it in the accent colour, with a chevron. */
|
|
11
28
|
label: string;
|
|
12
29
|
};
|
|
13
30
|
export type MenuComponent = (props: MenuProps) => ReactNode;
|
|
@@ -24,13 +24,16 @@ export type SheetHeaderProps = {
|
|
|
24
24
|
leadingItems?: NavigationBarItem[];
|
|
25
25
|
/** A search field in place of the title (iOS). */
|
|
26
26
|
search?: NavigationBarSearch;
|
|
27
|
+
/** The title — in the bar, or above the content with a `large` `titleStyle`. */
|
|
27
28
|
title?: string;
|
|
28
29
|
/** `center` by default. */
|
|
29
30
|
titleStyle?: SheetTitleStyle;
|
|
30
31
|
/** In screen order, toward the trailing edge — the close item, by convention. */
|
|
31
32
|
trailingItems?: NavigationBarItem[];
|
|
32
33
|
};
|
|
34
|
+
/** Imperative control of a `Sheet`, through its `ref`. */
|
|
33
35
|
export type SheetRef = {
|
|
36
|
+
/** Dismisses it without calling `onDismiss`, which reports the user's dismissals only. */
|
|
34
37
|
dismiss: () => Promise<void>;
|
|
35
38
|
/** Presents at a detent — the first by default. */
|
|
36
39
|
present: (detentIndex?: number) => Promise<void>;
|
|
@@ -45,6 +48,7 @@ export type SheetRef = {
|
|
|
45
48
|
export type SheetProps = {
|
|
46
49
|
/** The sheet's colour. `surface.primary` by default. */
|
|
47
50
|
bg?: ColorToken;
|
|
51
|
+
/** The content. A `<Sheet.Header>` among the direct children becomes the native header instead. */
|
|
48
52
|
children: ReactNode;
|
|
49
53
|
/** Where the sheet can rest, smallest first. `['auto']` — fit the content — by default. */
|
|
50
54
|
detents?: SheetDetent[];
|
|
@@ -60,29 +64,44 @@ export type SheetProps = {
|
|
|
60
64
|
grabber?: boolean;
|
|
61
65
|
/** The user dismissed it — `visible` going false does not call this. */
|
|
62
66
|
onDismiss: () => void;
|
|
67
|
+
/** The sheet moved to another detent — its index in `detents`. */
|
|
63
68
|
onDetentChange?: (detentIndex: number) => void;
|
|
69
|
+
/** The sheet finished presenting. */
|
|
64
70
|
onPresent?: () => void;
|
|
71
|
+
/** `present`, `dismiss` and `resize`, for driving the sheet imperatively. */
|
|
65
72
|
ref?: Ref<SheetRef>;
|
|
66
73
|
/**
|
|
67
74
|
* The content is a scroll view (`<Sheet.ScrollView>`) the sheet drives: dragging past its top resizes the sheet,
|
|
68
75
|
* and a header scrolls with the iOS 26 edge effect. Off by default — set it with a scrolling body.
|
|
69
76
|
*/
|
|
70
77
|
scrollable?: boolean;
|
|
78
|
+
/** Test identifier, forwarded to the native sheet. */
|
|
71
79
|
testID?: string;
|
|
80
|
+
/** Presents the sheet while true; going false dismisses it. */
|
|
72
81
|
visible: boolean;
|
|
73
82
|
};
|
|
74
83
|
export type SheetScrollHandler = (event: NativeSyntheticEvent<NativeScrollEvent>) => void;
|
|
75
84
|
/** What a scroll view inside a sheet needs: the room the header takes, the large title, the scroll listener. */
|
|
76
85
|
export type SheetScrollContextValue = {
|
|
86
|
+
/** Render it first in the content. `null` unless the header's `titleStyle` is `large`. */
|
|
77
87
|
largeTitle: ReactNode;
|
|
88
|
+
/** Forward the scroll view's scroll events to it — it tracks the large title. Unset when there is none. */
|
|
78
89
|
onScroll?: SheetScrollHandler;
|
|
90
|
+
/** The room an overlaid header takes, to add to the content's top padding. `0` where the header sits above it. */
|
|
79
91
|
topInset: number;
|
|
80
92
|
};
|
|
93
|
+
/**
|
|
94
|
+
* The body of a `scrollable` sheet: its content starts below an overlaid header, it carries a large title, and it
|
|
95
|
+
* reports its scroll to the sheet.
|
|
96
|
+
*/
|
|
81
97
|
export type SheetScrollViewProps = Pick<ScrollViewProps, 'keyboardShouldPersistTaps'> & {
|
|
98
|
+
/** The scrolling content, after the large title if there is one. */
|
|
82
99
|
children: ReactNode;
|
|
83
100
|
/** Styles the scrolling content — padding and gap belong here, as with `RnScrollView`. */
|
|
84
101
|
content?: ResponsiveViewSx;
|
|
102
|
+
/** Called on every scroll event, after the sheet's own listener. */
|
|
85
103
|
onScroll?: SheetScrollHandler;
|
|
104
|
+
/** Test identifier, forwarded to the scroll view. */
|
|
86
105
|
testID?: string;
|
|
87
106
|
};
|
|
88
107
|
export type SheetComponent = ((props: SheetProps) => ReactNode) & {
|
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* A short message laid over the bottom of its parent, with an optional action. The React Native tier, drawn the same on
|
|
4
|
+
* both platforms: it needs no host, and it never hides itself.
|
|
5
|
+
*/
|
|
2
6
|
export type SnackbarProps = {
|
|
7
|
+
/** The action button's text, on the trailing side. No button without it. */
|
|
3
8
|
actionLabel?: string;
|
|
9
|
+
/** The message. */
|
|
4
10
|
message: string;
|
|
11
|
+
/** The action was pressed. The snackbar stays up — turn `visible` off to hide it. */
|
|
5
12
|
onAction?: () => void;
|
|
13
|
+
/** Shows the snackbar while true, fading it in and out. It sets no timer of its own. */
|
|
6
14
|
visible: boolean;
|
|
7
15
|
};
|
|
8
16
|
export type SnackbarComponent = (props: SnackbarProps) => ReactNode;
|
|
@@ -1,20 +1,43 @@
|
|
|
1
|
+
import type { ColorToken } from '@hzblj/kara-theme';
|
|
1
2
|
import type { ReactNode } from 'react';
|
|
2
|
-
|
|
3
|
+
import type { IconName } from '../../atoms/icon';
|
|
4
|
+
/** One button a swiped row uncovers. */
|
|
5
|
+
export type SwipeAction = {
|
|
6
|
+
/** Fills the button with `fill.destructive.primary` unless `tint` says otherwise. */
|
|
7
|
+
destructive?: boolean;
|
|
8
|
+
/** One of the app's icons, on the button — a round one on iOS 26, as in Mail, a filled stadium on Android. */
|
|
9
|
+
icon: IconName;
|
|
10
|
+
/**
|
|
11
|
+
* The action's name, written under the button — before iOS 26 a row of the usual height shows the icon alone and
|
|
12
|
+
* keeps the label for VoiceOver. Actions are keyed by it, so keep labels unique within one row.
|
|
13
|
+
*/
|
|
14
|
+
label: string;
|
|
15
|
+
/** Called when the button is pressed. The row closes by itself. */
|
|
16
|
+
onPress: () => void;
|
|
17
|
+
/**
|
|
18
|
+
* The button's fill — `fill.destructive.primary` for a destructive action, `fill.accent.primary` otherwise. The icon
|
|
19
|
+
* on it is white, so pick a fill that holds white in both appearances.
|
|
20
|
+
*/
|
|
21
|
+
tint?: ColorToken;
|
|
22
|
+
};
|
|
3
23
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
24
|
+
* A list row with buttons behind it, uncovered by a swipe — SwiftUI's `swipeActions` on iOS, and on Android, where
|
|
25
|
+
* Compose has none, kara's own: the row slides on an anchored drag and the buttons show in the strip it leaves. One
|
|
26
|
+
* row is open at a time on both; a tap on an open row closes it. A native organism — it belongs inside a `List`.
|
|
7
27
|
*/
|
|
8
28
|
export type SwipeActionsProps = {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
/** Whether swiping all the way performs the first action in the group. */
|
|
29
|
+
/**
|
|
30
|
+
* iOS: a swipe all the way across performs the edge's first action, as in Mail. Android has no full swipe. `true`
|
|
31
|
+
* by default, as in SwiftUI.
|
|
32
|
+
*/
|
|
14
33
|
allowsFullSwipe?: boolean;
|
|
34
|
+
/** The row. */
|
|
15
35
|
children: ReactNode;
|
|
16
|
-
edge
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
36
|
+
/** The buttons at the leading edge, uncovered by a swipe toward the trailing one. The first sits outermost. */
|
|
37
|
+
leading?: SwipeAction[];
|
|
38
|
+
/** Test identifier. iOS: on the row. Android: the row is a Compose node, reach it by its content. */
|
|
39
|
+
testID?: string;
|
|
40
|
+
/** The buttons at the trailing edge, uncovered by a swipe toward the leading one. The first sits outermost. */
|
|
41
|
+
trailing?: SwipeAction[];
|
|
20
42
|
};
|
|
43
|
+
export type SwipeActionsComponent = (props: SwipeActionsProps) => ReactNode;
|
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
import type { Sx } from '@hzblj/kara-sx';
|
|
2
2
|
import type { ReactNode } from 'react';
|
|
3
|
+
/**
|
|
4
|
+
* The full-bleed native host a screen starts with — a SwiftUI or Compose `Host` filling its parent, so native atoms
|
|
5
|
+
* render anywhere inside it. The background runs to the edges; the content keeps clear of the status bar where the
|
|
6
|
+
* screen covers it (no navigation bar above), on Android as SwiftUI's safe area does on iOS.
|
|
7
|
+
*/
|
|
3
8
|
export type ScreenProps = {
|
|
9
|
+
/** The screen's colour. `surface.base` by default. */
|
|
4
10
|
bg?: Sx['bg'];
|
|
11
|
+
/** Laid out in a column that fills the screen. */
|
|
5
12
|
children?: ReactNode;
|
|
13
|
+
/** Keeps the content where it is when the keyboard opens, instead of avoiding it. Android reads it once, on mount. */
|
|
6
14
|
ignoreKeyboardInset?: boolean;
|
|
15
|
+
/** Test identifier — on the host view on iOS, on the view around it on Android. */
|
|
7
16
|
testID?: string;
|
|
8
17
|
};
|
|
9
18
|
export type ScreenComponent = (props: ScreenProps) => ReactNode;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hzblj/kara-components",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "The atoms, molecules, organisms and templates of @hzblj/kara-ui — real SwiftUI and Jetpack Compose views through @expo/ui.",
|
|
5
5
|
"author": "Jan Blazej",
|
|
6
6
|
"license": "MIT",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"bugs": {
|
|
13
13
|
"url": "https://github.com/hzblj/kara-ui/issues"
|
|
14
14
|
},
|
|
15
|
-
"homepage": "https://
|
|
15
|
+
"homepage": "https://www.kara.janblazej.dev/docs/components",
|
|
16
16
|
"keywords": [
|
|
17
17
|
"react-native",
|
|
18
18
|
"expo",
|
|
@@ -59,15 +59,14 @@
|
|
|
59
59
|
]
|
|
60
60
|
},
|
|
61
61
|
"dependencies": {
|
|
62
|
-
"@hzblj/kara-native": "^0.
|
|
63
|
-
"@hzblj/kara-sx": "^0.
|
|
64
|
-
"@hzblj/kara-theme": "^0.
|
|
62
|
+
"@hzblj/kara-native": "^0.8.0",
|
|
63
|
+
"@hzblj/kara-sx": "^0.8.0",
|
|
64
|
+
"@hzblj/kara-theme": "^0.8.0",
|
|
65
65
|
"sf-symbols-typescript": "^2.2.0"
|
|
66
66
|
},
|
|
67
67
|
"peerDependencies": {
|
|
68
68
|
"@expo/ui": "~57.0.0",
|
|
69
69
|
"@lodev09/react-native-true-sheet": ">=3.11.0",
|
|
70
|
-
"@shopify/flash-list": ">=2.0.0",
|
|
71
70
|
"expo": "~57.0.0",
|
|
72
71
|
"expo-image": "*",
|
|
73
72
|
"react": "*",
|
|
@@ -77,7 +76,6 @@
|
|
|
77
76
|
"devDependencies": {
|
|
78
77
|
"@expo/ui": "~57.0.18",
|
|
79
78
|
"@lodev09/react-native-true-sheet": "3.11.18",
|
|
80
|
-
"@shopify/flash-list": "^2.3.2",
|
|
81
79
|
"@storybook/react-native": "^10.6.0",
|
|
82
80
|
"@types/react": "~19.2.18",
|
|
83
81
|
"expo": "~57.0.22",
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import {Host, LoadingIndicator} from '@expo/ui/jetpack-compose'
|
|
2
|
+
import {fillMaxSize, testID as testIdModifier} from '@expo/ui/jetpack-compose/modifiers'
|
|
3
|
+
import {useAppTheme} from '@hzblj/kara-theme'
|
|
4
|
+
import {useMemo} from 'react'
|
|
5
|
+
import {StyleSheet} from 'react-native'
|
|
6
|
+
|
|
7
|
+
import type {ActivityIndicatorComponent, ActivityIndicatorProps} from './activity-indicator.types'
|
|
8
|
+
|
|
9
|
+
/** Material's 48dp for a screen; half of it in a row, about where React Native's small spinner sat. */
|
|
10
|
+
const styles = StyleSheet.create({
|
|
11
|
+
large: {height: 48, width: 48},
|
|
12
|
+
small: {height: 24, width: 24},
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Material 3's expressive `LoadingIndicator` in a host of its own, sized here rather than measured, so it renders
|
|
17
|
+
* anywhere and takes its place in the first frame.
|
|
18
|
+
*/
|
|
19
|
+
export const ActivityIndicator: ActivityIndicatorComponent = ({
|
|
20
|
+
color = 'fill.accent.primary',
|
|
21
|
+
size = 'small',
|
|
22
|
+
testID,
|
|
23
|
+
}: ActivityIndicatorProps) => {
|
|
24
|
+
const theme = useAppTheme()
|
|
25
|
+
const modifiers = useMemo(() => (testID ? [fillMaxSize(), testIdModifier(testID)] : [fillMaxSize()]), [testID])
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<Host colorScheme={theme.dark ? 'dark' : 'light'} seedColor={theme.materialSeed} style={styles[size]}>
|
|
29
|
+
<LoadingIndicator color={theme.color(color)} modifiers={modifiers} />
|
|
30
|
+
</Host>
|
|
31
|
+
)
|
|
32
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import {useAppTheme} from '@hzblj/kara-theme'
|
|
2
|
+
import {ActivityIndicator as RNActivityIndicator} from 'react-native'
|
|
3
|
+
|
|
4
|
+
import type {ActivityIndicatorComponent, ActivityIndicatorProps} from './activity-indicator.types'
|
|
5
|
+
|
|
6
|
+
/** React Native's spinner, which on iOS is UIKit's own `UIActivityIndicatorView`. */
|
|
7
|
+
export const ActivityIndicator: ActivityIndicatorComponent = ({
|
|
8
|
+
color = 'fill.accent.primary',
|
|
9
|
+
size = 'small',
|
|
10
|
+
testID,
|
|
11
|
+
}: ActivityIndicatorProps) => {
|
|
12
|
+
const theme = useAppTheme()
|
|
13
|
+
|
|
14
|
+
return <RNActivityIndicator color={theme.color(color)} size={size} testID={testID} />
|
|
15
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type {ColorToken} from '@hzblj/kara-theme'
|
|
2
|
+
import type {ReactNode} from 'react'
|
|
3
|
+
|
|
4
|
+
export type ActivityIndicatorSize = 'large' | 'small'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* That something is loading, for as long as it takes — UIKit's spinner on iOS, Material 3's expressive
|
|
8
|
+
* `LoadingIndicator` on Android, its shapes morphing as it turns. A React Native view on iOS and a host of its own on
|
|
9
|
+
* Android, so it renders anywhere. For how far along something is, use `Progress`.
|
|
10
|
+
*/
|
|
11
|
+
export type ActivityIndicatorProps = {
|
|
12
|
+
/** Its colour. `fill.accent.primary` by default. */
|
|
13
|
+
color?: ColorToken
|
|
14
|
+
/** `small` sits in a row or a button, `large` holds a screen that is loading. `small` by default. */
|
|
15
|
+
size?: ActivityIndicatorSize
|
|
16
|
+
/** Test identifier, forwarded to the view. */
|
|
17
|
+
testID?: string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type ActivityIndicatorComponent = (props: ActivityIndicatorProps) => ReactNode
|
|
@@ -32,7 +32,7 @@ export const Avatar: AvatarComponent = ({initials, size = 'md', source, testID}:
|
|
|
32
32
|
w={diameter}
|
|
33
33
|
>
|
|
34
34
|
{source ? (
|
|
35
|
-
<Image source={source}
|
|
35
|
+
<Image h={diameter} source={source} w={diameter} />
|
|
36
36
|
) : (
|
|
37
37
|
<RnText color="content.secondary" variant={VARIANT[size]}>
|
|
38
38
|
{initials ?? ''}
|
|
@@ -3,10 +3,15 @@ import type {ImageSourcePropType} from 'react-native'
|
|
|
3
3
|
|
|
4
4
|
export type AvatarSize = 'lg' | 'md' | 'sm'
|
|
5
5
|
|
|
6
|
+
/** A round avatar: the image in `source`, or `initials` without one. The React Native tier — it needs no host. */
|
|
6
7
|
export type AvatarProps = {
|
|
8
|
+
/** Shown in place of the image when there is no `source`. */
|
|
7
9
|
initials?: string
|
|
10
|
+
/** The diameter — `sm` is `s24`, `md` `s40`, `lg` `s64`. `md` by default. */
|
|
8
11
|
size?: AvatarSize
|
|
12
|
+
/** The image, cropped to the circle. */
|
|
9
13
|
source?: ImageSourcePropType
|
|
14
|
+
/** Test identifier, forwarded to the view. */
|
|
10
15
|
testID?: string
|
|
11
16
|
}
|
|
12
17
|
|
|
@@ -2,8 +2,11 @@ import type {ReactNode} from 'react'
|
|
|
2
2
|
|
|
3
3
|
export type BadgeTone = 'accent' | 'destructive' | 'neutral' | 'success'
|
|
4
4
|
|
|
5
|
+
/** A small status label in a capsule — a SwiftUI `HStack` in the tone's fill on iOS, Material's `Badge` on Android. */
|
|
5
6
|
export type BadgeProps = {
|
|
7
|
+
/** The text inside. */
|
|
6
8
|
label: string
|
|
9
|
+
/** The fill and text colours. `neutral` by default. */
|
|
7
10
|
tone?: BadgeTone
|
|
8
11
|
}
|
|
9
12
|
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import {BackdropBlurView} from '@hzblj/kara-native/jetpack-compose'
|
|
2
|
+
import {useSx} from '@hzblj/kara-sx'
|
|
3
|
+
import {type ColorToken, type OpacitySize, useAppTheme, withOpacity} from '@hzblj/kara-theme'
|
|
4
|
+
import {useMemo} from 'react'
|
|
5
|
+
import {StyleSheet} from 'react-native'
|
|
6
|
+
|
|
7
|
+
import type {BlurMaterial, BlurViewComponent, BlurViewProps} from './blur-view.types'
|
|
8
|
+
|
|
9
|
+
type MaterialSpec = {
|
|
10
|
+
/** dp. */
|
|
11
|
+
blur: number
|
|
12
|
+
/** A Material tonal surface — the thicker the material, the higher the container. */
|
|
13
|
+
fill: ColorToken
|
|
14
|
+
/**
|
|
15
|
+
* How much of the surface covers the blur, light and dark. A dark material covers more, as iOS's do: a dark surface
|
|
16
|
+
* over a bright backdrop otherwise leaves light content on light colour.
|
|
17
|
+
*/
|
|
18
|
+
opacity: {dark: OpacitySize; light: OpacitySize}
|
|
19
|
+
/** iOS's materials lift the colour of what they blur; a plain blur greys it into mud. */
|
|
20
|
+
saturation: number
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const MATERIALS: Record<BlurMaterial, MaterialSpec> = {
|
|
24
|
+
regular: {blur: 28, fill: 'surface.secondary', opacity: {dark: 'o75', light: 'o60'}, saturation: 1.6},
|
|
25
|
+
thick: {blur: 30, fill: 'surface.secondary', opacity: {dark: 'o85', light: 'o80'}, saturation: 1.4},
|
|
26
|
+
thin: {blur: 24, fill: 'surface.primary', opacity: {dark: 'o60', light: 'o40'}, saturation: 1.7},
|
|
27
|
+
ultraThick: {blur: 32, fill: 'fill.quaternary', opacity: {dark: 'o90', light: 'o90'}, saturation: 1.2},
|
|
28
|
+
ultraThin: {blur: 20, fill: 'surface.base', opacity: {dark: 'o50', light: 'o25'}, saturation: 1.8},
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* The materials in Android's terms: kara's `BackdropBlurView` — what lies behind, blurred and saturated — under a
|
|
33
|
+
* Material tonal surface that gets higher and more opaque as the material thickens. Pinned to the appearance the app
|
|
34
|
+
* is not in, the surface is Material's inverse one. `vibrancy` has no Android counterpart and is left out.
|
|
35
|
+
*/
|
|
36
|
+
export const BlurView: BlurViewComponent = ({
|
|
37
|
+
appearance,
|
|
38
|
+
children,
|
|
39
|
+
material = 'regular',
|
|
40
|
+
testID,
|
|
41
|
+
vibrancy: _vibrancy,
|
|
42
|
+
...sx
|
|
43
|
+
}: BlurViewProps) => {
|
|
44
|
+
const theme = useAppTheme()
|
|
45
|
+
const resolved = useSx(sx)
|
|
46
|
+
const spec = MATERIALS[material]
|
|
47
|
+
const inverted = appearance !== undefined && (appearance === 'dark') !== theme.dark
|
|
48
|
+
const dark = appearance ? appearance === 'dark' : theme.dark
|
|
49
|
+
const base = String(theme.color(inverted ? 'surface.high.contrast' : spec.fill))
|
|
50
|
+
const {cornerRadius, style} = useMemo(() => {
|
|
51
|
+
const {borderRadius, ...rest} = StyleSheet.flatten(resolved) ?? {}
|
|
52
|
+
return {cornerRadius: typeof borderRadius === 'number' ? borderRadius : 0, style: rest}
|
|
53
|
+
}, [resolved])
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<BackdropBlurView
|
|
57
|
+
blurRadius={spec.blur}
|
|
58
|
+
cornerRadius={cornerRadius}
|
|
59
|
+
fallbackTint={withOpacity(base, 'o95')}
|
|
60
|
+
glassTint={withOpacity(base, spec.opacity[dark ? 'dark' : 'light'])}
|
|
61
|
+
saturation={spec.saturation}
|
|
62
|
+
style={style}
|
|
63
|
+
testID={testID}
|
|
64
|
+
>
|
|
65
|
+
{children}
|
|
66
|
+
</BackdropBlurView>
|
|
67
|
+
)
|
|
68
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import {BlurView as NativeBlurView} from '@hzblj/kara-native/swift-ui'
|
|
2
|
+
import {useSx} from '@hzblj/kara-sx'
|
|
3
|
+
import {useAppTheme} from '@hzblj/kara-theme'
|
|
4
|
+
import {useMemo} from 'react'
|
|
5
|
+
import {StyleSheet} from 'react-native'
|
|
6
|
+
|
|
7
|
+
import type {BlurViewComponent, BlurViewProps} from './blur-view.types'
|
|
8
|
+
|
|
9
|
+
/** `borderRadius` is the material's shape, so it goes to the effect view — the one that clips — not the layout box. */
|
|
10
|
+
export const BlurView: BlurViewComponent = ({
|
|
11
|
+
appearance,
|
|
12
|
+
children,
|
|
13
|
+
material = 'regular',
|
|
14
|
+
testID,
|
|
15
|
+
vibrancy,
|
|
16
|
+
...sx
|
|
17
|
+
}: BlurViewProps) => {
|
|
18
|
+
const theme = useAppTheme()
|
|
19
|
+
const resolved = useSx(sx)
|
|
20
|
+
const {cornerRadius, style} = useMemo(() => {
|
|
21
|
+
const {borderRadius, ...rest} = StyleSheet.flatten(resolved) ?? {}
|
|
22
|
+
return {cornerRadius: typeof borderRadius === 'number' ? borderRadius : 0, style: rest}
|
|
23
|
+
}, [resolved])
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<NativeBlurView
|
|
27
|
+
colorScheme={appearance ?? (theme.dark ? 'dark' : 'light')}
|
|
28
|
+
cornerRadius={cornerRadius}
|
|
29
|
+
material={material}
|
|
30
|
+
style={style}
|
|
31
|
+
testID={testID}
|
|
32
|
+
vibrancy={vibrancy}
|
|
33
|
+
>
|
|
34
|
+
{children}
|
|
35
|
+
</NativeBlurView>
|
|
36
|
+
)
|
|
37
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type {BlurMaterial, BlurVibrancy} from '@hzblj/kara-native/swift-ui'
|
|
2
|
+
import type {ResponsiveViewSx} from '@hzblj/kara-sx'
|
|
3
|
+
import type {ReactNode} from 'react'
|
|
4
|
+
|
|
5
|
+
export type {BlurMaterial, BlurVibrancy} from '@hzblj/kara-native/swift-ui'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* One of the system's materials behind React Native content — what sheets, bars, menus and HUDs are made of, in
|
|
9
|
+
* SwiftUI's five thicknesses. iOS: UIKit's `system*Material` blur, with optional vibrancy for the content. Android:
|
|
10
|
+
* what lies behind, blurred and saturated (Android 12+), under a Material tonal surface as thick as the material —
|
|
11
|
+
* `surface` at `ultraThin` up to `surfaceContainerHighest` at `ultraThick`; below Android 12 the surface alone. The
|
|
12
|
+
* React Native tier: it needs no host, so it suits a list cell. Shape it with `borderRadius`; it clips its children.
|
|
13
|
+
*/
|
|
14
|
+
export type BlurViewProps = ResponsiveViewSx & {
|
|
15
|
+
/**
|
|
16
|
+
* Pins the material to light or dark whatever the app's appearance — a dark HUD over a light screen. Unset, it follows
|
|
17
|
+
* the app theme. Android draws the opposite appearance with Material's inverse surface.
|
|
18
|
+
*/
|
|
19
|
+
appearance?: 'dark' | 'light'
|
|
20
|
+
/** React Native content, over the material. */
|
|
21
|
+
children?: ReactNode
|
|
22
|
+
/** How much of what lies behind shows through, from `ultraThin` (most) to `ultraThick` (least). `regular` by default. */
|
|
23
|
+
material?: BlurMaterial
|
|
24
|
+
/** Test identifier, forwarded to the view. */
|
|
25
|
+
testID?: string
|
|
26
|
+
/**
|
|
27
|
+
* iOS: draws the children vibrant at this level, the way iOS draws its own labels and fills on a material — their
|
|
28
|
+
* colour becomes the material's vibrant one and their alpha the mask. Android has no vibrancy: the children keep
|
|
29
|
+
* their colours there.
|
|
30
|
+
*/
|
|
31
|
+
vibrancy?: BlurVibrancy
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export type BlurViewComponent = (props: BlurViewProps) => ReactNode
|
|
@@ -22,12 +22,22 @@ export type ButtonStyleName =
|
|
|
22
22
|
*/
|
|
23
23
|
export type ButtonControlSize = 'extraLarge' | 'large' | 'mini' | 'regular' | 'small'
|
|
24
24
|
|
|
25
|
+
/** A text button — SwiftUI `Button` on iOS, Material's filled button (outlined for `glass`) on Android. */
|
|
25
26
|
export type ButtonProps = {
|
|
27
|
+
/**
|
|
28
|
+
* Replaces the style the variant picks — `borderedProminent` for `accent` and `destructive`, `bordered` for
|
|
29
|
+
* `secondary`, `glass` for `glass`. iOS only — Android ignores it.
|
|
30
|
+
*/
|
|
26
31
|
buttonStyle?: ButtonStyleName
|
|
32
|
+
/** SwiftUI's control size. `large` by default. iOS only — Android ignores it. */
|
|
27
33
|
controlSize?: ButtonControlSize
|
|
34
|
+
/** Draws the button in its disabled colours and stops it responding to presses. */
|
|
28
35
|
disabled?: boolean
|
|
36
|
+
/** The button's text. */
|
|
29
37
|
label: string
|
|
38
|
+
/** Called when the button is pressed. */
|
|
30
39
|
onPress?: () => void
|
|
40
|
+
/** Test identifier, forwarded to the native view. */
|
|
31
41
|
testID?: string
|
|
32
42
|
/**
|
|
33
43
|
* Overrides the variant's own tint. A prominent style takes it as the fill and picks a contrasting
|
|
@@ -35,7 +45,9 @@ export type ButtonProps = {
|
|
|
35
45
|
* the stock `.glass` style — see the iOS implementation.
|
|
36
46
|
*/
|
|
37
47
|
tint?: ColorToken
|
|
48
|
+
/** The look. `accent` by default. Material has no glass, so on Android `glass` is an outlined button. */
|
|
38
49
|
variant?: ButtonVariant
|
|
50
|
+
/** Stretches the button to the full width it is offered. On by default. */
|
|
39
51
|
wide?: boolean
|
|
40
52
|
}
|
|
41
53
|
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import type {ResponsiveSx} from '@hzblj/kara-sx'
|
|
2
2
|
import type {ReactNode} from 'react'
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* A raised container — SwiftUI `VStack` / Compose `Column` on `surface.primary`, with a large radius, `s16` padding and
|
|
6
|
+
* the `card` shadow. Any `sx` prop overrides its default.
|
|
7
|
+
*/
|
|
4
8
|
export type CardProps = ResponsiveSx & {
|
|
9
|
+
/** Native views, stacked vertically from the leading edge. */
|
|
5
10
|
children?: ReactNode
|
|
11
|
+
/** Test identifier, forwarded to the native view. */
|
|
6
12
|
testID?: string
|
|
7
13
|
}
|
|
8
14
|
|