@hzblj/kara-components 0.20.1 → 0.22.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 +48 -0
- package/lib/module/atoms/activity-indicator/activity-indicator.android.js +1 -1
- package/lib/module/atoms/activity-indicator/activity-indicator.android.js.map +1 -1
- package/lib/module/atoms/activity-indicator/activity-indicator.ios.js +1 -1
- package/lib/module/atoms/activity-indicator/activity-indicator.ios.js.map +1 -1
- package/lib/module/atoms/badge/badge.android.js +1 -1
- package/lib/module/atoms/badge/badge.android.js.map +1 -1
- package/lib/module/atoms/badge/badge.ios.js +1 -1
- package/lib/module/atoms/badge/badge.ios.js.map +1 -1
- package/lib/module/atoms/blur-view/blur-view.android.js +22 -1
- package/lib/module/atoms/blur-view/blur-view.android.js.map +1 -1
- package/lib/module/atoms/blur-view/blur-view.ios.js +20 -0
- package/lib/module/atoms/blur-view/blur-view.ios.js.map +1 -1
- package/lib/module/atoms/button/button.android.js +1 -1
- package/lib/module/atoms/button/button.android.js.map +1 -1
- package/lib/module/atoms/button/button.ios.js +1 -1
- package/lib/module/atoms/button/button.ios.js.map +1 -1
- package/lib/module/atoms/card/card.android.js +1 -1
- package/lib/module/atoms/card/card.android.js.map +1 -1
- package/lib/module/atoms/card/card.ios.js +1 -1
- package/lib/module/atoms/card/card.ios.js.map +1 -1
- package/lib/module/atoms/checkbox/checkbox.android.js +1 -1
- package/lib/module/atoms/checkbox/checkbox.android.js.map +1 -1
- package/lib/module/atoms/checkbox/checkbox.ios.js +1 -1
- package/lib/module/atoms/checkbox/checkbox.ios.js.map +1 -1
- package/lib/module/atoms/color-picker/color-picker.android.js +1 -1
- package/lib/module/atoms/color-picker/color-picker.android.js.map +1 -1
- package/lib/module/atoms/color-picker/color-picker.ios.js +1 -1
- package/lib/module/atoms/color-picker/color-picker.ios.js.map +1 -1
- package/lib/module/atoms/date-picker/date-picker.android.js +1 -1
- package/lib/module/atoms/date-picker/date-picker.android.js.map +1 -1
- package/lib/module/atoms/date-picker/date-picker.ios.js +1 -1
- package/lib/module/atoms/date-picker/date-picker.ios.js.map +1 -1
- package/lib/module/atoms/divider/divider.android.js +1 -1
- package/lib/module/atoms/divider/divider.android.js.map +1 -1
- package/lib/module/atoms/divider/divider.ios.js +1 -1
- package/lib/module/atoms/divider/divider.ios.js.map +1 -1
- package/lib/module/atoms/fab/fab.android.js +1 -1
- package/lib/module/atoms/fab/fab.android.js.map +1 -1
- package/lib/module/atoms/fab/fab.ios.js +1 -1
- package/lib/module/atoms/fab/fab.ios.js.map +1 -1
- package/lib/module/atoms/glyph/glyph-button.android.js.map +1 -0
- package/lib/module/atoms/glyph/glyph-button.ios.js.map +1 -0
- package/lib/module/atoms/glyph/glyph-button.types.js.map +1 -0
- package/lib/module/atoms/glyph/glyph.android.js.map +1 -0
- package/lib/module/atoms/glyph/glyph.ios.js.map +1 -0
- package/lib/module/atoms/glyph/glyph.types.js.map +1 -0
- package/lib/module/atoms/glyph/index.js.map +1 -0
- package/lib/module/atoms/glyph/rn-glyph.android.js.map +1 -0
- package/lib/module/atoms/glyph/rn-glyph.ios.js.map +1 -0
- package/lib/module/atoms/h-stack/h-stack.android.js +2 -2
- package/lib/module/atoms/h-stack/h-stack.android.js.map +1 -1
- package/lib/module/atoms/h-stack/h-stack.ios.js +2 -2
- package/lib/module/atoms/h-stack/h-stack.ios.js.map +1 -1
- package/lib/module/atoms/icon/icon.android.js +1 -1
- package/lib/module/atoms/icon/icon.android.js.map +1 -1
- package/lib/module/atoms/icon/icon.ios.js +1 -1
- package/lib/module/atoms/icon/icon.ios.js.map +1 -1
- package/lib/module/atoms/icon-button/icon-button.android.js +1 -1
- package/lib/module/atoms/icon-button/icon-button.android.js.map +1 -1
- package/lib/module/atoms/icon-button/icon-button.ios.js +1 -1
- package/lib/module/atoms/icon-button/icon-button.ios.js.map +1 -1
- package/lib/module/atoms/island/island.android.js +3 -1
- package/lib/module/atoms/island/island.android.js.map +1 -1
- package/lib/module/atoms/island/island.ios.js +3 -1
- package/lib/module/atoms/island/island.ios.js.map +1 -1
- package/lib/module/atoms/list/list.android.js +2 -2
- package/lib/module/atoms/list/list.android.js.map +1 -1
- package/lib/module/atoms/list/list.ios.js +1 -1
- package/lib/module/atoms/list/list.ios.js.map +1 -1
- package/lib/module/atoms/picker/picker.android.js +2 -2
- package/lib/module/atoms/picker/picker.android.js.map +1 -1
- package/lib/module/atoms/picker/picker.ios.js +1 -1
- package/lib/module/atoms/picker/picker.ios.js.map +1 -1
- package/lib/module/atoms/pressable/pressable.js +1 -1
- package/lib/module/atoms/pressable/pressable.js.map +1 -1
- package/lib/module/atoms/progress/progress.android.js +1 -1
- package/lib/module/atoms/progress/progress.android.js.map +1 -1
- package/lib/module/atoms/progress/progress.ios.js +1 -1
- package/lib/module/atoms/progress/progress.ios.js.map +1 -1
- package/lib/module/atoms/scroll-view/scroll-view.android.js +2 -2
- package/lib/module/atoms/scroll-view/scroll-view.android.js.map +1 -1
- package/lib/module/atoms/scroll-view/scroll-view.ios.js +1 -1
- package/lib/module/atoms/scroll-view/scroll-view.ios.js.map +1 -1
- package/lib/module/atoms/segmented-control/segmented-control.android.js +1 -1
- package/lib/module/atoms/segmented-control/segmented-control.android.js.map +1 -1
- package/lib/module/atoms/segmented-control/segmented-control.ios.js +1 -1
- package/lib/module/atoms/segmented-control/segmented-control.ios.js.map +1 -1
- package/lib/module/atoms/slider/slider.android.js +1 -1
- package/lib/module/atoms/slider/slider.android.js.map +1 -1
- package/lib/module/atoms/slider/slider.ios.js +1 -1
- package/lib/module/atoms/slider/slider.ios.js.map +1 -1
- package/lib/module/atoms/spacer/spacer.android.js +1 -1
- package/lib/module/atoms/spacer/spacer.android.js.map +1 -1
- package/lib/module/atoms/spacer/spacer.ios.js +1 -1
- package/lib/module/atoms/spacer/spacer.ios.js.map +1 -1
- package/lib/module/atoms/surface/surface.android.js +1 -1
- package/lib/module/atoms/surface/surface.android.js.map +1 -1
- package/lib/module/atoms/surface/surface.ios.js +1 -1
- package/lib/module/atoms/surface/surface.ios.js.map +1 -1
- package/lib/module/atoms/switch/switch.android.js +1 -1
- package/lib/module/atoms/switch/switch.android.js.map +1 -1
- package/lib/module/atoms/switch/switch.ios.js +1 -1
- package/lib/module/atoms/switch/switch.ios.js.map +1 -1
- package/lib/module/atoms/text/text.android.js +1 -1
- package/lib/module/atoms/text/text.android.js.map +1 -1
- package/lib/module/atoms/text/text.ios.js +1 -1
- package/lib/module/atoms/text/text.ios.js.map +1 -1
- package/lib/module/atoms/text-field/text-field.android.js +2 -2
- package/lib/module/atoms/text-field/text-field.android.js.map +1 -1
- package/lib/module/atoms/text-field/text-field.ios.js +1 -1
- package/lib/module/atoms/text-field/text-field.ios.js.map +1 -1
- package/lib/module/atoms/v-stack/v-stack.android.js +2 -2
- package/lib/module/atoms/v-stack/v-stack.android.js.map +1 -1
- package/lib/module/atoms/v-stack/v-stack.ios.js +2 -2
- package/lib/module/atoms/v-stack/v-stack.ios.js.map +1 -1
- package/lib/module/atoms/z-stack/z-stack.android.js +2 -2
- package/lib/module/atoms/z-stack/z-stack.android.js.map +1 -1
- package/lib/module/atoms/z-stack/z-stack.ios.js +2 -2
- package/lib/module/atoms/z-stack/z-stack.ios.js.map +1 -1
- package/lib/module/hooks/host-context.js.map +1 -0
- package/lib/module/hooks/use-refresh-state.js.map +1 -0
- package/lib/module/hooks/use-stack-modifiers.js.map +1 -0
- package/lib/module/molecules/collapsible/collapsible.android.js +2 -2
- package/lib/module/molecules/collapsible/collapsible.android.js.map +1 -1
- package/lib/module/molecules/collapsible/collapsible.ios.js +1 -1
- package/lib/module/molecules/collapsible/collapsible.ios.js.map +1 -1
- package/lib/module/molecules/labeled-content/labeled-content.js +1 -1
- package/lib/module/molecules/labeled-content/labeled-content.js.map +1 -1
- package/lib/module/molecules/list-item/list-item.js +1 -1
- package/lib/module/molecules/list-item/list-item.js.map +1 -1
- package/lib/module/molecules/navigation-bar/navigation-bar.constants.js +5 -0
- package/lib/module/molecules/navigation-bar/navigation-bar.constants.js.map +1 -0
- package/lib/module/molecules/navigation-bar/navigation-bar.ios.js +31 -4
- package/lib/module/molecules/navigation-bar/navigation-bar.ios.js.map +1 -1
- package/lib/module/molecules/pager/pager.android.js +1 -1
- package/lib/module/molecules/pager/pager.android.js.map +1 -1
- package/lib/module/molecules/pager/pager.ios.js +1 -1
- package/lib/module/molecules/pager/pager.ios.js.map +1 -1
- package/lib/module/molecules/radio-group/radio-group.android.js +1 -1
- package/lib/module/molecules/radio-group/radio-group.android.js.map +1 -1
- package/lib/module/molecules/radio-group/radio-group.ios.js +2 -2
- package/lib/module/molecules/radio-group/radio-group.ios.js.map +1 -1
- package/lib/module/molecules/rn-list-item/rn-list-item.js +1 -1
- package/lib/module/molecules/rn-list-item/rn-list-item.js.map +1 -1
- package/lib/module/molecules/rn-tooltip/rn-tooltip.android.js +1 -1
- package/lib/module/molecules/rn-tooltip/rn-tooltip.android.js.map +1 -1
- package/lib/module/molecules/search-bar/search-bar.js +1 -1
- package/lib/module/molecules/search-bar/search-bar.js.map +1 -1
- package/lib/module/molecules/stepper/stepper.android.js +2 -2
- package/lib/module/molecules/stepper/stepper.android.js.map +1 -1
- package/lib/module/molecules/stepper/stepper.ios.js +1 -1
- package/lib/module/molecules/stepper/stepper.ios.js.map +1 -1
- package/lib/module/molecules/tooltip/tooltip.android.js +1 -1
- package/lib/module/molecules/tooltip/tooltip.android.js.map +1 -1
- package/lib/module/molecules/tooltip/tooltip.ios.js +1 -1
- package/lib/module/molecules/tooltip/tooltip.ios.js.map +1 -1
- package/lib/module/organisms/action-sheet/action-sheet.android.js +1 -1
- package/lib/module/organisms/action-sheet/action-sheet.android.js.map +1 -1
- package/lib/module/organisms/action-sheet/action-sheet.ios.js +1 -1
- package/lib/module/organisms/action-sheet/action-sheet.ios.js.map +1 -1
- package/lib/module/organisms/alert/alert.android.js +1 -1
- package/lib/module/organisms/alert/alert.android.js.map +1 -1
- package/lib/module/organisms/alert/alert.ios.js +1 -1
- package/lib/module/organisms/alert/alert.ios.js.map +1 -1
- package/lib/module/organisms/context-menu/context-menu.android.js +2 -2
- package/lib/module/organisms/context-menu/context-menu.android.js.map +1 -1
- package/lib/module/organisms/fab-menu/fab-menu.android.js +2 -2
- package/lib/module/organisms/fab-menu/fab-menu.android.js.map +1 -1
- package/lib/module/organisms/fab-menu/fab-menu.ios.js +2 -2
- package/lib/module/organisms/fab-menu/fab-menu.ios.js.map +1 -1
- package/lib/module/organisms/form/form.android.js +1 -1
- package/lib/module/organisms/form/form.android.js.map +1 -1
- package/lib/module/organisms/form/form.ios.js +1 -1
- package/lib/module/organisms/form/form.ios.js.map +1 -1
- package/lib/module/organisms/header/header-color.js +8 -0
- package/lib/module/organisms/header/header-color.js.map +1 -0
- package/lib/module/organisms/header/header-inset.android.js +6 -0
- package/lib/module/organisms/header/header-inset.android.js.map +1 -0
- package/lib/module/organisms/header/header-inset.ios.js +19 -0
- package/lib/module/organisms/header/header-inset.ios.js.map +1 -0
- package/lib/module/organisms/header/header-large-title-fade.android.js +6 -0
- package/lib/module/organisms/header/header-large-title-fade.android.js.map +1 -0
- package/lib/module/organisms/header/header-large-title-fade.ios.js +15 -0
- package/lib/module/organisms/header/header-large-title-fade.ios.js.map +1 -0
- package/lib/module/organisms/header/header-options.android.js +180 -0
- package/lib/module/organisms/header/header-options.android.js.map +1 -0
- package/lib/module/organisms/header/header-options.ios.js +145 -0
- package/lib/module/organisms/header/header-options.ios.js.map +1 -0
- package/lib/module/organisms/header/header-options.types.js +4 -0
- package/lib/module/organisms/header/header-options.types.js.map +1 -0
- package/lib/module/organisms/header/header-search-accessory.android.js +5 -0
- package/lib/module/organisms/header/header-search-accessory.android.js.map +1 -0
- package/lib/module/organisms/header/header-search-accessory.ios.js +35 -0
- package/lib/module/organisms/header/header-search-accessory.ios.js.map +1 -0
- package/lib/module/organisms/header/header-search-accessory.types.js +4 -0
- package/lib/module/organisms/header/header-search-accessory.types.js.map +1 -0
- package/lib/module/organisms/header/header-search-input.android.js +80 -0
- package/lib/module/organisms/header/header-search-input.android.js.map +1 -0
- package/lib/module/organisms/header/header-search-input.ios.js +8 -0
- package/lib/module/organisms/header/header-search-input.ios.js.map +1 -0
- package/lib/module/organisms/header/header-search-input.types.js +4 -0
- package/lib/module/organisms/header/header-search-input.types.js.map +1 -0
- package/lib/module/organisms/header/header-search-scrim.js +62 -0
- package/lib/module/organisms/header/header-search-scrim.js.map +1 -0
- package/lib/module/organisms/header/header-title.js +230 -0
- package/lib/module/organisms/header/header-title.js.map +1 -0
- package/lib/module/organisms/header/header.types.js +4 -0
- package/lib/module/organisms/header/header.types.js.map +1 -0
- package/lib/module/organisms/header/hero-header-background.android.js +33 -0
- package/lib/module/organisms/header/hero-header-background.android.js.map +1 -0
- package/lib/module/organisms/header/hero-header-background.ios.js +40 -0
- package/lib/module/organisms/header/hero-header-background.ios.js.map +1 -0
- package/lib/module/organisms/header/hero-header-background.types.js +4 -0
- package/lib/module/organisms/header/hero-header-background.types.js.map +1 -0
- package/lib/module/organisms/header/index.js +14 -0
- package/lib/module/organisms/header/index.js.map +1 -0
- package/lib/module/organisms/header/native-subtitle.android.js +5 -0
- package/lib/module/organisms/header/native-subtitle.android.js.map +1 -0
- package/lib/module/organisms/header/native-subtitle.ios.js +22 -0
- package/lib/module/organisms/header/native-subtitle.ios.js.map +1 -0
- package/lib/module/organisms/header/native-subtitle.types.js +4 -0
- package/lib/module/organisms/header/native-subtitle.types.js.map +1 -0
- package/lib/module/organisms/header/sheet-grabber.android.js +5 -0
- package/lib/module/organisms/header/sheet-grabber.android.js.map +1 -0
- package/lib/module/organisms/header/sheet-grabber.ios.js +15 -0
- package/lib/module/organisms/header/sheet-grabber.ios.js.map +1 -0
- package/lib/module/organisms/header/sheet-grabber.types.js +4 -0
- package/lib/module/organisms/header/sheet-grabber.types.js.map +1 -0
- package/lib/module/organisms/header/use-header-bar-inset.android.js +4 -0
- package/lib/module/organisms/header/use-header-bar-inset.android.js.map +1 -0
- package/lib/module/organisms/header/use-header-bar-inset.ios.js +9 -0
- package/lib/module/organisms/header/use-header-bar-inset.ios.js.map +1 -0
- package/lib/module/organisms/header/use-header-bar-inset.types.js +2 -0
- package/lib/module/organisms/header/use-header-bar-inset.types.js.map +1 -0
- package/lib/module/organisms/header/use-header-scroll-to-top.js +51 -0
- package/lib/module/organisms/header/use-header-scroll-to-top.js.map +1 -0
- package/lib/module/organisms/header/use-header-search.js +135 -0
- package/lib/module/organisms/header/use-header-search.js.map +1 -0
- package/lib/module/organisms/header/use-header.js +74 -0
- package/lib/module/organisms/header/use-header.js.map +1 -0
- package/lib/module/organisms/header/use-hero-header.js +189 -0
- package/lib/module/organisms/header/use-hero-header.js.map +1 -0
- package/lib/module/organisms/header/use-stack-screen-options.js +77 -0
- package/lib/module/organisms/header/use-stack-screen-options.js.map +1 -0
- package/lib/module/organisms/menu/menu.android.js +2 -2
- package/lib/module/organisms/menu/menu.android.js.map +1 -1
- package/lib/module/organisms/menu/menu.ios.js +1 -1
- package/lib/module/organisms/menu/menu.ios.js.map +1 -1
- package/lib/module/organisms/sheet/use-sheet-header.js +3 -1
- package/lib/module/organisms/sheet/use-sheet-header.js.map +1 -1
- package/lib/module/organisms/snackbar/snackbar.js +1 -1
- package/lib/module/organisms/snackbar/snackbar.js.map +1 -1
- package/lib/module/organisms/swipe-actions/swipe-actions.android.js +1 -1
- package/lib/module/organisms/swipe-actions/swipe-actions.android.js.map +1 -1
- package/lib/module/organisms/swipe-actions/swipe-actions.ios.js +1 -1
- package/lib/module/organisms/swipe-actions/swipe-actions.ios.js.map +1 -1
- package/lib/module/templates/screen/screen.android.js +3 -3
- package/lib/module/templates/screen/screen.android.js.map +1 -1
- package/lib/module/templates/screen/screen.ios.js +3 -3
- package/lib/module/templates/screen/screen.ios.js.map +1 -1
- package/lib/module/utils/ripple-clip.js.map +1 -0
- package/lib/module/utils/tap-modifiers.js.map +1 -0
- package/lib/typescript/atoms/blur-view/blur-view.types.d.ts +8 -0
- package/lib/typescript/atoms/island/island.types.d.ts +17 -0
- package/lib/typescript/molecules/navigation-bar/navigation-bar.constants.d.ts +2 -0
- package/lib/typescript/molecules/navigation-bar/navigation-bar.types.d.ts +67 -4
- package/lib/typescript/organisms/header/header-color.d.ts +6 -0
- package/lib/typescript/organisms/header/header-inset.d.ts +1 -0
- package/lib/typescript/organisms/header/header-inset.ios.d.ts +2 -0
- package/lib/typescript/organisms/header/header-large-title-fade.d.ts +1 -0
- package/lib/typescript/organisms/header/header-large-title-fade.ios.d.ts +7 -0
- package/lib/typescript/organisms/header/header-options.d.ts +1 -0
- package/lib/typescript/organisms/header/header-options.ios.d.ts +2 -0
- package/lib/typescript/organisms/header/header-options.types.d.ts +4 -0
- package/lib/typescript/organisms/header/header-search-accessory.d.ts +1 -0
- package/lib/typescript/organisms/header/header-search-accessory.ios.d.ts +6 -0
- package/lib/typescript/organisms/header/header-search-accessory.types.d.ts +9 -0
- package/lib/typescript/organisms/header/header-search-input.d.ts +1 -0
- package/lib/typescript/organisms/header/header-search-input.ios.d.ts +5 -0
- package/lib/typescript/organisms/header/header-search-input.types.d.ts +17 -0
- package/lib/typescript/organisms/header/header-search-scrim.d.ts +11 -0
- package/lib/typescript/organisms/header/header-title.d.ts +22 -0
- package/lib/typescript/organisms/header/header.types.d.ts +445 -0
- package/lib/typescript/organisms/header/hero-header-background.d.ts +1 -0
- package/lib/typescript/organisms/header/hero-header-background.ios.d.ts +7 -0
- package/lib/typescript/organisms/header/hero-header-background.types.d.ts +9 -0
- package/lib/typescript/organisms/header/index.d.ts +12 -0
- package/lib/typescript/organisms/header/native-subtitle.d.ts +1 -0
- package/lib/typescript/organisms/header/native-subtitle.ios.d.ts +4 -0
- package/lib/typescript/organisms/header/native-subtitle.types.d.ts +6 -0
- package/lib/typescript/organisms/header/sheet-grabber.d.ts +1 -0
- package/lib/typescript/organisms/header/sheet-grabber.ios.d.ts +7 -0
- package/lib/typescript/organisms/header/sheet-grabber.types.d.ts +6 -0
- package/lib/typescript/organisms/header/use-header-bar-inset.d.ts +1 -0
- package/lib/typescript/organisms/header/use-header-bar-inset.ios.d.ts +1 -0
- package/lib/typescript/organisms/header/use-header-bar-inset.types.d.ts +6 -0
- package/lib/typescript/organisms/header/use-header-scroll-to-top.d.ts +8 -0
- package/lib/typescript/organisms/header/use-header-search.d.ts +12 -0
- package/lib/typescript/organisms/header/use-header.d.ts +14 -0
- package/lib/typescript/organisms/header/use-hero-header.d.ts +10 -0
- package/lib/typescript/organisms/header/use-stack-screen-options.d.ts +17 -0
- package/lib/typescript/templates/screen/screen.types.d.ts +6 -2
- package/package.json +24 -5
- package/src/atoms/activity-indicator/activity-indicator.android.tsx +1 -1
- package/src/atoms/activity-indicator/activity-indicator.ios.tsx +1 -1
- package/src/atoms/badge/badge.android.tsx +1 -1
- package/src/atoms/badge/badge.ios.tsx +1 -1
- package/src/atoms/blur-view/blur-view.android.tsx +28 -1
- package/src/atoms/blur-view/blur-view.ios.tsx +25 -0
- package/src/atoms/blur-view/blur-view.types.ts +8 -0
- package/src/atoms/button/button.android.tsx +1 -1
- package/src/atoms/button/button.ios.tsx +1 -1
- package/src/atoms/card/card.android.tsx +1 -1
- package/src/atoms/card/card.ios.tsx +1 -1
- package/src/atoms/checkbox/checkbox.android.tsx +1 -1
- package/src/atoms/checkbox/checkbox.ios.tsx +1 -1
- package/src/atoms/color-picker/color-picker.android.tsx +1 -1
- package/src/atoms/color-picker/color-picker.ios.tsx +1 -1
- package/src/atoms/date-picker/date-picker.android.tsx +1 -1
- package/src/atoms/date-picker/date-picker.ios.tsx +1 -1
- package/src/atoms/divider/divider.android.tsx +1 -1
- package/src/atoms/divider/divider.ios.tsx +1 -1
- package/src/atoms/fab/fab.android.tsx +1 -1
- package/src/atoms/fab/fab.ios.tsx +1 -1
- package/src/atoms/h-stack/h-stack.android.tsx +2 -2
- package/src/atoms/h-stack/h-stack.ios.tsx +2 -2
- package/src/atoms/icon/icon.android.tsx +1 -1
- package/src/atoms/icon/icon.ios.tsx +1 -1
- package/src/atoms/icon-button/icon-button.android.tsx +1 -1
- package/src/atoms/icon-button/icon-button.ios.tsx +1 -1
- package/src/atoms/island/island.android.tsx +3 -1
- package/src/atoms/island/island.ios.tsx +10 -2
- package/src/atoms/island/island.types.ts +18 -0
- package/src/atoms/list/list.android.tsx +2 -2
- package/src/atoms/list/list.ios.tsx +1 -1
- package/src/atoms/picker/picker.android.tsx +2 -3
- package/src/atoms/picker/picker.ios.tsx +1 -1
- package/src/atoms/pressable/pressable.tsx +1 -1
- package/src/atoms/progress/progress.android.tsx +1 -1
- package/src/atoms/progress/progress.ios.tsx +1 -1
- package/src/atoms/scroll-view/scroll-view.android.tsx +2 -2
- package/src/atoms/scroll-view/scroll-view.ios.tsx +1 -1
- package/src/atoms/segmented-control/segmented-control.android.tsx +1 -1
- package/src/atoms/segmented-control/segmented-control.ios.tsx +1 -1
- package/src/atoms/slider/slider.android.tsx +1 -1
- package/src/atoms/slider/slider.ios.tsx +1 -1
- package/src/atoms/spacer/spacer.android.tsx +1 -1
- package/src/atoms/spacer/spacer.ios.tsx +1 -1
- package/src/atoms/surface/surface.android.tsx +1 -1
- package/src/atoms/surface/surface.ios.tsx +1 -1
- package/src/atoms/switch/switch.android.tsx +1 -1
- package/src/atoms/switch/switch.ios.tsx +1 -1
- package/src/atoms/text/text.android.tsx +1 -1
- package/src/atoms/text/text.ios.tsx +1 -1
- package/src/atoms/text-field/text-field.android.tsx +2 -2
- package/src/atoms/text-field/text-field.ios.tsx +1 -1
- package/src/atoms/v-stack/v-stack.android.tsx +2 -2
- package/src/atoms/v-stack/v-stack.ios.tsx +2 -2
- package/src/atoms/z-stack/z-stack.android.tsx +2 -2
- package/src/atoms/z-stack/z-stack.ios.tsx +2 -2
- package/src/molecules/collapsible/collapsible.android.tsx +2 -2
- package/src/molecules/collapsible/collapsible.ios.tsx +1 -1
- package/src/molecules/labeled-content/labeled-content.tsx +1 -1
- package/src/molecules/list-item/list-item.tsx +1 -2
- package/src/molecules/navigation-bar/navigation-bar.constants.ts +2 -0
- package/src/molecules/navigation-bar/navigation-bar.ios.tsx +35 -5
- package/src/molecules/navigation-bar/navigation-bar.types.ts +69 -5
- package/src/molecules/pager/pager.android.tsx +1 -1
- package/src/molecules/pager/pager.ios.tsx +1 -1
- package/src/molecules/radio-group/radio-group.android.tsx +1 -1
- package/src/molecules/radio-group/radio-group.ios.tsx +2 -2
- package/src/molecules/rn-list-item/rn-list-item.tsx +1 -1
- package/src/molecules/rn-tooltip/rn-tooltip.android.tsx +1 -1
- package/src/molecules/search-bar/search-bar.tsx +1 -1
- package/src/molecules/stepper/stepper.android.tsx +2 -2
- package/src/molecules/stepper/stepper.ios.tsx +1 -1
- package/src/molecules/tooltip/tooltip.android.tsx +1 -1
- package/src/molecules/tooltip/tooltip.ios.tsx +1 -1
- package/src/organisms/action-sheet/action-sheet.android.tsx +1 -1
- package/src/organisms/action-sheet/action-sheet.ios.tsx +1 -1
- package/src/organisms/alert/alert.android.tsx +1 -1
- package/src/organisms/alert/alert.ios.tsx +1 -1
- package/src/organisms/context-menu/context-menu.android.tsx +2 -2
- package/src/organisms/fab-menu/fab-menu.android.tsx +2 -2
- package/src/organisms/fab-menu/fab-menu.ios.tsx +2 -3
- package/src/organisms/form/form.android.tsx +1 -1
- package/src/organisms/form/form.ios.tsx +1 -1
- package/src/organisms/header/header-color.ts +7 -0
- package/src/organisms/header/header-inset.android.tsx +3 -0
- package/src/organisms/header/header-inset.ios.tsx +12 -0
- package/src/organisms/header/header-large-title-fade.android.tsx +3 -0
- package/src/organisms/header/header-large-title-fade.ios.tsx +12 -0
- package/src/organisms/header/header-options.android.tsx +168 -0
- package/src/organisms/header/header-options.ios.tsx +138 -0
- package/src/organisms/header/header-options.types.ts +6 -0
- package/src/organisms/header/header-search-accessory.android.tsx +4 -0
- package/src/organisms/header/header-search-accessory.ios.tsx +42 -0
- package/src/organisms/header/header-search-accessory.types.ts +12 -0
- package/src/organisms/header/header-search-input.android.tsx +89 -0
- package/src/organisms/header/header-search-input.ios.tsx +7 -0
- package/src/organisms/header/header-search-input.types.ts +21 -0
- package/src/organisms/header/header-search-scrim.tsx +49 -0
- package/src/organisms/header/header-title.tsx +220 -0
- package/src/organisms/header/header.types.ts +423 -0
- package/src/organisms/header/hero-header-background.android.tsx +22 -0
- package/src/organisms/header/hero-header-background.ios.tsx +26 -0
- package/src/organisms/header/hero-header-background.types.ts +11 -0
- package/src/organisms/header/index.ts +12 -0
- package/src/organisms/header/native-subtitle.android.tsx +5 -0
- package/src/organisms/header/native-subtitle.ios.tsx +22 -0
- package/src/organisms/header/native-subtitle.types.ts +8 -0
- package/src/organisms/header/sheet-grabber.android.tsx +4 -0
- package/src/organisms/header/sheet-grabber.ios.tsx +12 -0
- package/src/organisms/header/sheet-grabber.types.ts +8 -0
- package/src/organisms/header/use-header-bar-inset.android.ts +1 -0
- package/src/organisms/header/use-header-bar-inset.ios.ts +7 -0
- package/src/organisms/header/use-header-bar-inset.types.ts +6 -0
- package/src/organisms/header/use-header-scroll-to-top.ts +48 -0
- package/src/organisms/header/use-header-search.tsx +138 -0
- package/src/organisms/header/use-header.ts +67 -0
- package/src/organisms/header/use-hero-header.tsx +222 -0
- package/src/organisms/header/use-stack-screen-options.ts +66 -0
- package/src/organisms/menu/menu.android.tsx +2 -2
- package/src/organisms/menu/menu.ios.tsx +1 -1
- package/src/organisms/sheet/use-sheet-header.tsx +4 -1
- package/src/organisms/snackbar/snackbar.tsx +1 -1
- package/src/organisms/swipe-actions/swipe-actions.android.tsx +1 -1
- package/src/organisms/swipe-actions/swipe-actions.ios.tsx +1 -1
- package/src/templates/screen/screen.android.tsx +8 -3
- package/src/templates/screen/screen.ios.tsx +8 -3
- package/src/templates/screen/screen.types.ts +7 -2
- package/lib/module/glyph/glyph-button.android.js.map +0 -1
- package/lib/module/glyph/glyph-button.ios.js.map +0 -1
- package/lib/module/glyph/glyph-button.types.js.map +0 -1
- package/lib/module/glyph/glyph.android.js.map +0 -1
- package/lib/module/glyph/glyph.ios.js.map +0 -1
- package/lib/module/glyph/glyph.types.js.map +0 -1
- package/lib/module/glyph/index.js.map +0 -1
- package/lib/module/glyph/rn-glyph.android.js.map +0 -1
- package/lib/module/glyph/rn-glyph.ios.js.map +0 -1
- package/lib/module/host-context.js.map +0 -1
- package/lib/module/ripple-clip.js.map +0 -1
- package/lib/module/tap-modifiers.js.map +0 -1
- package/lib/module/use-refresh-state.js.map +0 -1
- package/lib/module/use-stack-modifiers.js.map +0 -1
- /package/lib/module/{glyph → atoms/glyph}/glyph-button.android.js +0 -0
- /package/lib/module/{glyph → atoms/glyph}/glyph-button.ios.js +0 -0
- /package/lib/module/{glyph → atoms/glyph}/glyph-button.types.js +0 -0
- /package/lib/module/{glyph → atoms/glyph}/glyph.android.js +0 -0
- /package/lib/module/{glyph → atoms/glyph}/glyph.ios.js +0 -0
- /package/lib/module/{glyph → atoms/glyph}/glyph.types.js +0 -0
- /package/lib/module/{glyph → atoms/glyph}/index.js +0 -0
- /package/lib/module/{glyph → atoms/glyph}/rn-glyph.android.js +0 -0
- /package/lib/module/{glyph → atoms/glyph}/rn-glyph.ios.js +0 -0
- /package/lib/module/{host-context.js → hooks/host-context.js} +0 -0
- /package/lib/module/{use-refresh-state.js → hooks/use-refresh-state.js} +0 -0
- /package/lib/module/{use-stack-modifiers.js → hooks/use-stack-modifiers.js} +0 -0
- /package/lib/module/{ripple-clip.js → utils/ripple-clip.js} +0 -0
- /package/lib/module/{tap-modifiers.js → utils/tap-modifiers.js} +0 -0
- /package/lib/typescript/{glyph → atoms/glyph}/glyph-button.d.ts +0 -0
- /package/lib/typescript/{glyph → atoms/glyph}/glyph-button.ios.d.ts +0 -0
- /package/lib/typescript/{glyph → atoms/glyph}/glyph-button.types.d.ts +0 -0
- /package/lib/typescript/{glyph → atoms/glyph}/glyph.d.ts +0 -0
- /package/lib/typescript/{glyph → atoms/glyph}/glyph.ios.d.ts +0 -0
- /package/lib/typescript/{glyph → atoms/glyph}/glyph.types.d.ts +0 -0
- /package/lib/typescript/{glyph → atoms/glyph}/index.d.ts +0 -0
- /package/lib/typescript/{glyph → atoms/glyph}/rn-glyph.d.ts +0 -0
- /package/lib/typescript/{glyph → atoms/glyph}/rn-glyph.ios.d.ts +0 -0
- /package/lib/typescript/{host-context.d.ts → hooks/host-context.d.ts} +0 -0
- /package/lib/typescript/{use-refresh-state.d.ts → hooks/use-refresh-state.d.ts} +0 -0
- /package/lib/typescript/{use-stack-modifiers.d.ts → hooks/use-stack-modifiers.d.ts} +0 -0
- /package/lib/typescript/{ripple-clip.d.ts → utils/ripple-clip.d.ts} +0 -0
- /package/lib/typescript/{tap-modifiers.d.ts → utils/tap-modifiers.d.ts} +0 -0
- /package/src/{glyph → atoms/glyph}/glyph-button.android.tsx +0 -0
- /package/src/{glyph → atoms/glyph}/glyph-button.ios.tsx +0 -0
- /package/src/{glyph → atoms/glyph}/glyph-button.types.ts +0 -0
- /package/src/{glyph → atoms/glyph}/glyph.android.tsx +0 -0
- /package/src/{glyph → atoms/glyph}/glyph.ios.tsx +0 -0
- /package/src/{glyph → atoms/glyph}/glyph.types.ts +0 -0
- /package/src/{glyph → atoms/glyph}/index.ts +0 -0
- /package/src/{glyph → atoms/glyph}/rn-glyph.android.tsx +0 -0
- /package/src/{glyph → atoms/glyph}/rn-glyph.ios.tsx +0 -0
- /package/src/{host-context.tsx → hooks/host-context.tsx} +0 -0
- /package/src/{use-refresh-state.ts → hooks/use-refresh-state.ts} +0 -0
- /package/src/{use-stack-modifiers.ts → hooks/use-stack-modifiers.ts} +0 -0
- /package/src/{ripple-clip.ts → utils/ripple-clip.ts} +0 -0
- /package/src/{tap-modifiers.ts → utils/tap-modifiers.ts} +0 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type {ReactNode} from 'react'
|
|
2
|
+
import type {SharedValue} from 'react-native-reanimated'
|
|
3
|
+
|
|
4
|
+
export type HeroHeaderBackgroundProps = {
|
|
5
|
+
/** The bar's height, status bar included. */
|
|
6
|
+
height: number
|
|
7
|
+
/** How far the header has faded in: 0 over the hero, 1 revealed. */
|
|
8
|
+
progress: SharedValue<number>
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type HeroHeaderBackgroundComponent = (props: HeroHeaderBackgroundProps) => ReactNode
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export * from './header.types'
|
|
2
|
+
export * from './header-inset'
|
|
3
|
+
export * from './header-large-title-fade'
|
|
4
|
+
export {useIsTitleRevealed} from './header-title'
|
|
5
|
+
export * from './sheet-grabber'
|
|
6
|
+
export type {SheetGrabberProps} from './sheet-grabber.types'
|
|
7
|
+
export * from './use-header'
|
|
8
|
+
export * from './use-header-bar-inset'
|
|
9
|
+
export * from './use-header-scroll-to-top'
|
|
10
|
+
export * from './use-header-search'
|
|
11
|
+
export * from './use-hero-header'
|
|
12
|
+
export * from './use-stack-screen-options'
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import {isScrollEdgeEffectAvailable} from '@hzblj/kara-native'
|
|
2
|
+
import {NavigationItemSubtitle} from '@hzblj/kara-native/swift-ui'
|
|
3
|
+
import {useAppTheme} from '@hzblj/kara-theme'
|
|
4
|
+
|
|
5
|
+
import type {NativeSubtitleComponent, NativeSubtitleProps} from './native-subtitle.types'
|
|
6
|
+
|
|
7
|
+
/** iOS 26 puts a title and its subtitle on the leading side itself (`UINavigationItem.style = .browser`). */
|
|
8
|
+
export const hasNativeSubtitle = isScrollEdgeEffectAvailable()
|
|
9
|
+
|
|
10
|
+
export const NativeSubtitle: NativeSubtitleComponent = ({subtitle, subtitleHidden}: NativeSubtitleProps) => {
|
|
11
|
+
const theme = useAppTheme()
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<NavigationItemSubtitle
|
|
15
|
+
color={theme.color('content.secondary')}
|
|
16
|
+
fontFamily={theme.typography.label.fontFamily}
|
|
17
|
+
fontSize={theme.typography.label.fontSize}
|
|
18
|
+
subtitle={subtitle}
|
|
19
|
+
subtitleHidden={subtitleHidden}
|
|
20
|
+
/>
|
|
21
|
+
)
|
|
22
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import {SheetGrabber as NativeSheetGrabber} from '@hzblj/kara-native/swift-ui'
|
|
2
|
+
|
|
3
|
+
import type {SheetGrabberComponent, SheetGrabberProps} from './sheet-grabber.types'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The system grabber of a native-stack screen presented as a sheet (`presentation: 'modal'`), which react-native-screens
|
|
7
|
+
* gives only `formSheet`. Render it anywhere in the presented content, a nested stack's screens included; it takes no
|
|
8
|
+
* room. Android draws no grabber on a full-screen modal, and this renders nothing there.
|
|
9
|
+
*/
|
|
10
|
+
export const SheetGrabber: SheetGrabberComponent = ({visible = true}: SheetGrabberProps) => (
|
|
11
|
+
<NativeSheetGrabber grabberVisible={visible} />
|
|
12
|
+
)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const useHeaderBarInset = () => 0
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import {isScrollEdgeEffectAvailable} from '@hzblj/kara-native'
|
|
2
|
+
import {useSafeAreaInsets} from 'react-native-safe-area-context'
|
|
3
|
+
|
|
4
|
+
/** iOS 26's navigation bar is 54pt, the 44pt row of items plus room for its glass; before it the bar was the row. */
|
|
5
|
+
const BAR_HEIGHT = isScrollEdgeEffectAvailable() ? 54 : 44
|
|
6
|
+
|
|
7
|
+
export const useHeaderBarInset = () => useSafeAreaInsets().top + BAR_HEIGHT
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How far a transparent header reaches into the screen: the status bar plus the bar's own height on iOS, 0 on Android,
|
|
3
|
+
* where the top app bar sits above the content. Known on the first render, unlike a measured header height, so content
|
|
4
|
+
* laid out by it never jumps once the bar reports its size.
|
|
5
|
+
*/
|
|
6
|
+
export type UseHeaderBarInset = () => number
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import {type RefObject, useMemo} from 'react'
|
|
2
|
+
import {Platform} from 'react-native'
|
|
3
|
+
|
|
4
|
+
import type {HeaderScrollable, HeaderScrollToTopOptions, HeaderScrollToTopRef} from './header.types'
|
|
5
|
+
import {useHeaderBarInset} from './use-header-bar-inset'
|
|
6
|
+
|
|
7
|
+
/** The row iOS gives a large title, which the top of a large-title screen includes. */
|
|
8
|
+
const LARGE_TITLE_HEIGHT = 52
|
|
9
|
+
|
|
10
|
+
const scrollToOffset = (target: HeaderScrollable, offset: number) => {
|
|
11
|
+
if ('scrollToOffset' in target) {
|
|
12
|
+
target.scrollToOffset({animated: true, offset})
|
|
13
|
+
return
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if ('scrollTo' in target) {
|
|
17
|
+
target.scrollTo({animated: true, y: offset})
|
|
18
|
+
return
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
target.getScrollResponder()?.scrollTo({animated: true, y: offset})
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* A ref for React Navigation's or Expo Router's `useScrollToTop`: pressing the tab again scrolls the list back under a
|
|
26
|
+
* transparent header, with a large title expanded, where scrolling to 0 would leave the content under the bar and the
|
|
27
|
+
* title collapsed. `useScrollToTop(useHeaderScrollToTop(listRef, {largeTitle: true}))`.
|
|
28
|
+
*/
|
|
29
|
+
export const useHeaderScrollToTop = (
|
|
30
|
+
ref: RefObject<HeaderScrollable | null>,
|
|
31
|
+
{largeTitle = false, topInset = 0}: HeaderScrollToTopOptions = {}
|
|
32
|
+
): HeaderScrollToTopRef => {
|
|
33
|
+
const barInset = useHeaderBarInset()
|
|
34
|
+
const offset = Platform.OS === 'ios' ? -(barInset + (largeTitle ? LARGE_TITLE_HEIGHT : 0) + topInset) : 0
|
|
35
|
+
|
|
36
|
+
return useMemo(
|
|
37
|
+
() => ({
|
|
38
|
+
current: {
|
|
39
|
+
scrollToTop: () => {
|
|
40
|
+
if (ref.current) {
|
|
41
|
+
scrollToOffset(ref.current, offset)
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
}),
|
|
46
|
+
[offset, ref]
|
|
47
|
+
)
|
|
48
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import {useAppTheme} from '@hzblj/kara-theme'
|
|
2
|
+
import {useCallback, useMemo, useRef, useState} from 'react'
|
|
3
|
+
import {Platform} from 'react-native'
|
|
4
|
+
import type {SearchBarCommands} from 'react-native-screens'
|
|
5
|
+
|
|
6
|
+
import type {HeaderSearch, HeaderSearchBarOptions, HeaderSearchParams, HeaderSearchPlacement} from './header.types'
|
|
7
|
+
import {headerColor} from './header-color'
|
|
8
|
+
import {HeaderSearchAccessory} from './header-search-accessory'
|
|
9
|
+
import {HEADER_SEARCH_ROW_HEIGHT, HeaderSearchInput} from './header-search-input'
|
|
10
|
+
import type {HeaderSearchInputRef} from './header-search-input.types'
|
|
11
|
+
import {HeaderSearchScrim} from './header-search-scrim'
|
|
12
|
+
|
|
13
|
+
/** `integratedButton` hides the title and items while the field is open and keeps it out of the bottom toolbar. */
|
|
14
|
+
const PLACEMENT_OPTIONS: Record<HeaderSearchPlacement, Partial<HeaderSearchBarOptions>> = {
|
|
15
|
+
integratedButton: {allowToolbarIntegration: false, hideNavigationBar: true},
|
|
16
|
+
stacked: {hideNavigationBar: false},
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* A search field for a screen's header. iOS: UIKit's search bar stacked under the title, with the title and items kept
|
|
21
|
+
* and nothing dimmed by the system, so a list can filter live, or behind a search button in the bar's corner
|
|
22
|
+
* (`placement`); pass `searchBar` to `useHeader` as `search`. Android: Material's search field with its cancel button,
|
|
23
|
+
* as a row for the screen to put right under the top app bar (`searchInput`). Both report the `query`; `scrim` washes
|
|
24
|
+
* the content out while the field is focused and empty and, on iOS, carries the field's glyphs and voice.
|
|
25
|
+
*
|
|
26
|
+
* With `voice` the field has a microphone; the app runs the dictation and writes what it hears with `setText`. Typing
|
|
27
|
+
* over it, or ending the search, calls `voice.onCancel`.
|
|
28
|
+
*/
|
|
29
|
+
export const useHeaderSearch = ({
|
|
30
|
+
cancelIcon,
|
|
31
|
+
clearIcon,
|
|
32
|
+
enabled = true,
|
|
33
|
+
placeholder = 'Search',
|
|
34
|
+
placement = 'stacked',
|
|
35
|
+
voice,
|
|
36
|
+
}: HeaderSearchParams = {}): HeaderSearch => {
|
|
37
|
+
const theme = useAppTheme()
|
|
38
|
+
const [query, setQuery] = useState('')
|
|
39
|
+
const [focused, setFocused] = useState(false)
|
|
40
|
+
const searchBarRef = useRef<SearchBarCommands | null>(null)
|
|
41
|
+
const inputRef = useRef<HeaderSearchInputRef>(null)
|
|
42
|
+
const lastSetText = useRef<string | null>(null)
|
|
43
|
+
const voiceRef = useRef(voice)
|
|
44
|
+
|
|
45
|
+
voiceRef.current = voice
|
|
46
|
+
|
|
47
|
+
const cancelVoice = useCallback(() => {
|
|
48
|
+
if (voiceRef.current?.listening) {
|
|
49
|
+
voiceRef.current.onCancel?.()
|
|
50
|
+
}
|
|
51
|
+
}, [])
|
|
52
|
+
|
|
53
|
+
const changeQuery = useCallback(
|
|
54
|
+
(text: string) => {
|
|
55
|
+
if (text !== lastSetText.current) {
|
|
56
|
+
cancelVoice()
|
|
57
|
+
}
|
|
58
|
+
setQuery(text)
|
|
59
|
+
},
|
|
60
|
+
[cancelVoice]
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
const reset = useCallback(() => {
|
|
64
|
+
cancelVoice()
|
|
65
|
+
lastSetText.current = null
|
|
66
|
+
setQuery('')
|
|
67
|
+
setFocused(false)
|
|
68
|
+
}, [cancelVoice])
|
|
69
|
+
|
|
70
|
+
const searchBar = useMemo((): HeaderSearchBarOptions | undefined => {
|
|
71
|
+
if (!enabled || Platform.OS !== 'ios') {
|
|
72
|
+
return undefined
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
...PLACEMENT_OPTIONS[placement],
|
|
77
|
+
autoCapitalize: 'none',
|
|
78
|
+
hideWhenScrolling: false,
|
|
79
|
+
obscureBackground: false,
|
|
80
|
+
onBlur: () => setFocused(false),
|
|
81
|
+
onCancelButtonPress: reset,
|
|
82
|
+
onChangeText: event => changeQuery(event.nativeEvent.text),
|
|
83
|
+
onFocus: () => setFocused(true),
|
|
84
|
+
placeholder,
|
|
85
|
+
placement,
|
|
86
|
+
ref: searchBarRef,
|
|
87
|
+
tintColor: headerColor(theme, 'content.primary'),
|
|
88
|
+
}
|
|
89
|
+
}, [changeQuery, enabled, placeholder, placement, reset, theme])
|
|
90
|
+
|
|
91
|
+
const searchInput = useMemo(() => {
|
|
92
|
+
if (!enabled || Platform.OS !== 'android') {
|
|
93
|
+
return null
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return (
|
|
97
|
+
<HeaderSearchInput
|
|
98
|
+
clearIcon={clearIcon}
|
|
99
|
+
onChangeText={changeQuery}
|
|
100
|
+
onFocusChange={setFocused}
|
|
101
|
+
placeholder={placeholder}
|
|
102
|
+
ref={inputRef}
|
|
103
|
+
voice={voice}
|
|
104
|
+
/>
|
|
105
|
+
)
|
|
106
|
+
}, [changeQuery, clearIcon, enabled, placeholder, voice])
|
|
107
|
+
|
|
108
|
+
const clear = useCallback(() => {
|
|
109
|
+
reset()
|
|
110
|
+
searchBarRef.current?.cancelSearch()
|
|
111
|
+
inputRef.current?.clear()
|
|
112
|
+
inputRef.current?.blur()
|
|
113
|
+
}, [reset])
|
|
114
|
+
|
|
115
|
+
const setText = useCallback((text: string) => {
|
|
116
|
+
lastSetText.current = text
|
|
117
|
+
setQuery(text)
|
|
118
|
+
searchBarRef.current?.setText(text)
|
|
119
|
+
inputRef.current?.setText(text)
|
|
120
|
+
}, [])
|
|
121
|
+
|
|
122
|
+
const dimmed = enabled && focused && query.trim().length === 0
|
|
123
|
+
|
|
124
|
+
const scrim = useMemo(() => {
|
|
125
|
+
if (!enabled) {
|
|
126
|
+
return null
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return (
|
|
130
|
+
<>
|
|
131
|
+
{dimmed ? <HeaderSearchScrim onPress={clear} topOffset={HEADER_SEARCH_ROW_HEIGHT} /> : null}
|
|
132
|
+
<HeaderSearchAccessory cancelIcon={cancelIcon} clearIcon={clearIcon} voice={voice} />
|
|
133
|
+
</>
|
|
134
|
+
)
|
|
135
|
+
}, [cancelIcon, clear, clearIcon, dimmed, enabled, voice])
|
|
136
|
+
|
|
137
|
+
return {clear, query, scrim, searchBar, searchInput, setText}
|
|
138
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import {useAppTheme} from '@hzblj/kara-theme'
|
|
2
|
+
import {useLayoutEffect, useMemo} from 'react'
|
|
3
|
+
|
|
4
|
+
import type {HeaderContent, HeaderNavigation, HeaderOptions, HeaderOptionsResult, HeaderParams} from './header.types'
|
|
5
|
+
import {toHeaderOptions} from './header-options'
|
|
6
|
+
import {useHeaderTitle} from './header-title'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* A screen's header as native-stack options, with what its list needs alongside: the title (centred, leading with a
|
|
10
|
+
* subtitle, revealed on scroll or large), the actions on both sides (the navigation bar's own items on iOS, Material
|
|
11
|
+
* buttons on Android) and a search field. Spread `options` into Expo Router's `<Stack.Screen options>` or a React
|
|
12
|
+
* Navigation screen's `options`. Memoize the action arrays: new arrays build new options on every render.
|
|
13
|
+
*/
|
|
14
|
+
export const useHeaderOptions = ({
|
|
15
|
+
backHidden,
|
|
16
|
+
largeTitle,
|
|
17
|
+
leadingActions,
|
|
18
|
+
search,
|
|
19
|
+
subtitle,
|
|
20
|
+
title,
|
|
21
|
+
titleHidden = false,
|
|
22
|
+
titleRevealProgress,
|
|
23
|
+
trailingActions,
|
|
24
|
+
}: HeaderParams): HeaderOptionsResult => {
|
|
25
|
+
const theme = useAppTheme()
|
|
26
|
+
const headerTitle = useHeaderTitle({largeTitle, subtitle, title, titleHidden, titleRevealProgress})
|
|
27
|
+
|
|
28
|
+
const actionOptions = useMemo(
|
|
29
|
+
() => toHeaderOptions({backHidden, leadingActions, trailingActions}, theme),
|
|
30
|
+
[backHidden, leadingActions, theme, trailingActions]
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
return useMemo(() => {
|
|
34
|
+
const options: HeaderOptions = {}
|
|
35
|
+
|
|
36
|
+
Object.assign(options, headerTitle.options, actionOptions)
|
|
37
|
+
|
|
38
|
+
if (search) {
|
|
39
|
+
options.headerSearchBarOptions = search
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
contentInsetAdjustmentBehavior: 'automatic',
|
|
44
|
+
largeTitle: headerTitle.largeTitle,
|
|
45
|
+
onScroll: headerTitle.onScroll,
|
|
46
|
+
options,
|
|
47
|
+
}
|
|
48
|
+
}, [actionOptions, headerTitle.largeTitle, headerTitle.onScroll, headerTitle.options, search])
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Sets the screen's header through its navigation object, from React Navigation's or Expo Router's `useNavigation()`,
|
|
53
|
+
* before the screen first paints, and returns what its list needs. What a screen has from the moment it is pushed
|
|
54
|
+
* belongs in its static `options` too: iOS morphs the bar items between screens only when they exist at the push.
|
|
55
|
+
*/
|
|
56
|
+
export const useHeader = (navigation: HeaderNavigation, params: HeaderParams): HeaderContent => {
|
|
57
|
+
const {contentInsetAdjustmentBehavior, largeTitle, onScroll, options} = useHeaderOptions(params)
|
|
58
|
+
|
|
59
|
+
useLayoutEffect(() => {
|
|
60
|
+
navigation.setOptions(options)
|
|
61
|
+
}, [navigation, options])
|
|
62
|
+
|
|
63
|
+
return useMemo(
|
|
64
|
+
() => ({contentInsetAdjustmentBehavior, largeTitle, onScroll}),
|
|
65
|
+
[contentInsetAdjustmentBehavior, largeTitle, onScroll]
|
|
66
|
+
)
|
|
67
|
+
}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import {isScrollEdgeEffectAvailable, isViewControllerBasedStatusBarAppearance} from '@hzblj/kara-native'
|
|
2
|
+
import {useAppTheme} from '@hzblj/kara-theme'
|
|
3
|
+
import {useCallback, useEffect, useLayoutEffect, useMemo, useState} from 'react'
|
|
4
|
+
import {Platform, StatusBar} from 'react-native'
|
|
5
|
+
import {
|
|
6
|
+
Extrapolation,
|
|
7
|
+
interpolate,
|
|
8
|
+
type SharedValue,
|
|
9
|
+
useAnimatedReaction,
|
|
10
|
+
useDerivedValue,
|
|
11
|
+
useSharedValue,
|
|
12
|
+
} from 'react-native-reanimated'
|
|
13
|
+
import {useSafeAreaInsets} from 'react-native-safe-area-context'
|
|
14
|
+
import {scheduleOnRN} from 'react-native-worklets'
|
|
15
|
+
|
|
16
|
+
import type {
|
|
17
|
+
HeaderAction,
|
|
18
|
+
HeaderOptions,
|
|
19
|
+
HeroHeader,
|
|
20
|
+
HeroHeaderNavigation,
|
|
21
|
+
HeroHeaderParams,
|
|
22
|
+
HeroHeaderStyle,
|
|
23
|
+
} from './header.types'
|
|
24
|
+
import {headerColor} from './header-color'
|
|
25
|
+
import {useIsTitleRevealed} from './header-title'
|
|
26
|
+
import {HeroHeaderBackground} from './hero-header-background'
|
|
27
|
+
import {useHeaderOptions} from './use-header'
|
|
28
|
+
import {useHeaderBarInset} from './use-header-bar-inset'
|
|
29
|
+
import {HERO_HEADER_SCREEN_OPTIONS} from './use-stack-screen-options'
|
|
30
|
+
|
|
31
|
+
/** The header fades in while 50 to 70 % of the hero's part under the bar scrolls away. */
|
|
32
|
+
const REVEAL_RANGE = [0.5, 0.7] as const
|
|
33
|
+
|
|
34
|
+
/** Material 3's top app bar. */
|
|
35
|
+
const ANDROID_BAR_HEIGHT = 64
|
|
36
|
+
|
|
37
|
+
const useHeroBarHeight = () => {
|
|
38
|
+
const iosBarInset = useHeaderBarInset()
|
|
39
|
+
const {top} = useSafeAreaInsets()
|
|
40
|
+
|
|
41
|
+
return Platform.OS === 'ios' ? iosBarInset : top + ANDROID_BAR_HEIGHT
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const useIsOverHero = (progress: SharedValue<number>) => {
|
|
45
|
+
const [overHero, setOverHero] = useState(() => progress.value === 0)
|
|
46
|
+
|
|
47
|
+
useAnimatedReaction(
|
|
48
|
+
() => progress.value === 0,
|
|
49
|
+
(isOver, wasOver) => {
|
|
50
|
+
if (isOver !== wasOver) {
|
|
51
|
+
scheduleOnRN(setOverHero, isOver)
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
return overHero
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const useIsScreenFocused = (navigation: HeroHeaderNavigation) => {
|
|
60
|
+
const [focused, setFocused] = useState(() => navigation.isFocused())
|
|
61
|
+
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
const unsubscribeFocus = navigation.addListener('focus', () => setFocused(true))
|
|
64
|
+
const unsubscribeBlur = navigation.addListener('blur', () => setFocused(false))
|
|
65
|
+
|
|
66
|
+
return () => {
|
|
67
|
+
unsubscribeFocus()
|
|
68
|
+
unsubscribeBlur()
|
|
69
|
+
}
|
|
70
|
+
}, [navigation])
|
|
71
|
+
|
|
72
|
+
return focused
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const BAR_STYLES = {dark: 'dark-content', light: 'light-content'} as const satisfies Record<HeroHeaderStyle, string>
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* The status bar over the hero, for this screen only. With view-controller-based appearance (Info.plist) the native
|
|
79
|
+
* stack sets it per screen (`statusBarStyle`); otherwise, as in Expo apps and on Android, React Native's `StatusBar`
|
|
80
|
+
* applies it while the screen is focused and over the hero, and its stack restores the previous style once it is not.
|
|
81
|
+
*/
|
|
82
|
+
const useHeroStatusBar = (navigation: HeroHeaderNavigation, overHero: boolean, overHeroStyle: HeroHeaderStyle) => {
|
|
83
|
+
const focused = useIsScreenFocused(navigation)
|
|
84
|
+
const perScreen = isViewControllerBasedStatusBarAppearance()
|
|
85
|
+
|
|
86
|
+
useEffect(() => {
|
|
87
|
+
if (perScreen) {
|
|
88
|
+
navigation.setOptions({statusBarStyle: overHero ? overHeroStyle : 'auto'})
|
|
89
|
+
}
|
|
90
|
+
}, [navigation, overHero, overHeroStyle, perScreen])
|
|
91
|
+
|
|
92
|
+
if (perScreen || !focused || !overHero) {
|
|
93
|
+
return null
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return <StatusBar animated barStyle={BAR_STYLES[overHeroStyle]} />
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const useRevealProgress = (barHeight: number, heroHeight: number, introProgress?: SharedValue<number>) => {
|
|
100
|
+
const scrollY = useSharedValue(0)
|
|
101
|
+
const heroUnderBar = heroHeight - barHeight
|
|
102
|
+
const start = heroUnderBar * REVEAL_RANGE[0]
|
|
103
|
+
const end = heroUnderBar * REVEAL_RANGE[1]
|
|
104
|
+
|
|
105
|
+
const scrollProgress = useDerivedValue(() => {
|
|
106
|
+
if (end <= start) {
|
|
107
|
+
return 1
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return interpolate(scrollY.value, [start, end], [0, 1], Extrapolation.CLAMP)
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
const progress = useDerivedValue(() => Math.max(scrollProgress.value, introProgress?.value ?? 0))
|
|
114
|
+
|
|
115
|
+
const onScroll = useCallback(
|
|
116
|
+
(offsetY: number) => {
|
|
117
|
+
'worklet'
|
|
118
|
+
scrollY.value = offsetY
|
|
119
|
+
},
|
|
120
|
+
[scrollY]
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
return {onScroll, progress, scrollProgress}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* A header over a full-bleed hero: the content runs under a transparent bar, and while 50 to 70 % of the hero's part
|
|
128
|
+
* under the bar scrolls away the header fades in — the title (and subtitle) in the bar, the bar's background
|
|
129
|
+
* (`background`: iOS 26's scroll edge effect switched on, the bar's material coming in below iOS 26, the top app bar's
|
|
130
|
+
* surface on Android), the items' tint and the status bar, light over the hero with `overHeroStyle` `light`. `revealedTrailingActions` join the
|
|
131
|
+
* bar once the title is shown. Give the screen `HERO_HEADER_SCREEN_OPTIONS` as static options, so its first frame has
|
|
132
|
+
* no large title, render `background` after the list and call `onScroll` with its offset.
|
|
133
|
+
*/
|
|
134
|
+
export const useHeroHeader = (
|
|
135
|
+
navigation: HeroHeaderNavigation,
|
|
136
|
+
{
|
|
137
|
+
backHidden,
|
|
138
|
+
heroHeight,
|
|
139
|
+
introProgress,
|
|
140
|
+
leadingActions,
|
|
141
|
+
overHeroStyle = 'light',
|
|
142
|
+
revealedTrailingActions,
|
|
143
|
+
subtitle,
|
|
144
|
+
title,
|
|
145
|
+
trailingActions,
|
|
146
|
+
}: HeroHeaderParams
|
|
147
|
+
): HeroHeader => {
|
|
148
|
+
const theme = useAppTheme()
|
|
149
|
+
const barHeight = useHeroBarHeight()
|
|
150
|
+
const {onScroll, progress, scrollProgress} = useRevealProgress(barHeight, heroHeight, introProgress)
|
|
151
|
+
const titleShown = useIsTitleRevealed(scrollProgress)
|
|
152
|
+
const overHero = useIsOverHero(progress)
|
|
153
|
+
const scrolledOverHero = useIsOverHero(scrollProgress)
|
|
154
|
+
const statusBar = useHeroStatusBar(navigation, overHero, overHeroStyle)
|
|
155
|
+
|
|
156
|
+
const visibleTrailingActions = useMemo(() => {
|
|
157
|
+
if (!titleShown || !revealedTrailingActions) {
|
|
158
|
+
return trailingActions
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const actions: HeaderAction[] = []
|
|
162
|
+
|
|
163
|
+
for (const action of revealedTrailingActions) {
|
|
164
|
+
actions.push(action)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
for (const action of trailingActions ?? []) {
|
|
168
|
+
actions.push(action)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return actions
|
|
172
|
+
}, [revealedTrailingActions, titleShown, trailingActions])
|
|
173
|
+
|
|
174
|
+
const {options} = useHeaderOptions({
|
|
175
|
+
backHidden,
|
|
176
|
+
leadingActions,
|
|
177
|
+
subtitle,
|
|
178
|
+
title,
|
|
179
|
+
titleRevealProgress: scrollProgress,
|
|
180
|
+
trailingActions: visibleTrailingActions,
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
useLayoutEffect(() => {
|
|
184
|
+
navigation.setOptions(HERO_HEADER_SCREEN_OPTIONS)
|
|
185
|
+
}, [navigation])
|
|
186
|
+
|
|
187
|
+
useLayoutEffect(() => {
|
|
188
|
+
navigation.setOptions(options)
|
|
189
|
+
}, [navigation, options])
|
|
190
|
+
|
|
191
|
+
useEffect(() => {
|
|
192
|
+
const light = overHero && overHeroStyle === 'light'
|
|
193
|
+
const appearance: HeaderOptions = {
|
|
194
|
+
headerTintColor: headerColor(theme, light ? 'content.on.accent' : 'content.primary'),
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (isScrollEdgeEffectAvailable()) {
|
|
198
|
+
appearance.scrollEdgeEffects = {top: scrolledOverHero ? 'hidden' : 'automatic'}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
navigation.setOptions(appearance)
|
|
202
|
+
}, [navigation, overHero, overHeroStyle, scrolledOverHero, theme])
|
|
203
|
+
|
|
204
|
+
const background = useMemo(
|
|
205
|
+
() => (
|
|
206
|
+
<>
|
|
207
|
+
<HeroHeaderBackground height={barHeight} progress={progress} />
|
|
208
|
+
{statusBar}
|
|
209
|
+
</>
|
|
210
|
+
),
|
|
211
|
+
[barHeight, progress, statusBar]
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
return {
|
|
215
|
+
background,
|
|
216
|
+
contentInsetAdjustmentBehavior: 'never',
|
|
217
|
+
largeTitle: null,
|
|
218
|
+
onScroll,
|
|
219
|
+
progress,
|
|
220
|
+
scrollProgress,
|
|
221
|
+
}
|
|
222
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import {isScrollEdgeEffectAvailable} from '@hzblj/kara-native'
|
|
2
|
+
import {useAppTheme} from '@hzblj/kara-theme'
|
|
3
|
+
import {useMemo} from 'react'
|
|
4
|
+
import {Platform} from 'react-native'
|
|
5
|
+
|
|
6
|
+
import type {StackScreenOptions} from './header.types'
|
|
7
|
+
import {headerColor} from './header-color'
|
|
8
|
+
|
|
9
|
+
/** iOS 26 draws its own scroll edge effect under a transparent bar; older iOS needs the bar's blur instead. */
|
|
10
|
+
const HEADER_BLUR_EFFECT =
|
|
11
|
+
Platform.OS === 'ios' && !isScrollEdgeEffectAvailable() ? ('systemChromeMaterial' as const) : undefined
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* A native stack's `screenOptions` in the active theme: the surface behind every screen, a minimal back button, the
|
|
15
|
+
* title and large title in the theme's `headline` and `headingM`, no hairline under the bar. iOS keeps the bar
|
|
16
|
+
* transparent so iOS 26 blurs what scrolls under it (older iOS gets the bar's material); Android gets a solid top app
|
|
17
|
+
* bar in the surface colour.
|
|
18
|
+
*/
|
|
19
|
+
export const useStackScreenOptions = (): StackScreenOptions => {
|
|
20
|
+
const theme = useAppTheme()
|
|
21
|
+
|
|
22
|
+
return useMemo(() => {
|
|
23
|
+
const content = headerColor(theme, 'content.primary')
|
|
24
|
+
const surface = headerColor(theme, 'surface.base')
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
contentStyle: {backgroundColor: surface},
|
|
28
|
+
headerBackButtonDisplayMode: 'minimal',
|
|
29
|
+
headerBlurEffect: HEADER_BLUR_EFFECT,
|
|
30
|
+
headerLargeTitleShadowVisible: false,
|
|
31
|
+
headerLargeTitleStyle: {
|
|
32
|
+
color: content,
|
|
33
|
+
fontFamily: theme.typography.headingM.fontFamily,
|
|
34
|
+
fontSize: theme.typography.headingM.fontSize,
|
|
35
|
+
},
|
|
36
|
+
headerShadowVisible: false,
|
|
37
|
+
headerStyle: Platform.OS === 'android' ? {backgroundColor: surface} : undefined,
|
|
38
|
+
headerTintColor: content,
|
|
39
|
+
headerTitleStyle: {
|
|
40
|
+
color: content,
|
|
41
|
+
fontFamily: theme.typography.headline.fontFamily,
|
|
42
|
+
fontSize: theme.typography.headline.fontSize,
|
|
43
|
+
},
|
|
44
|
+
headerTransparent: Platform.OS === 'ios',
|
|
45
|
+
}
|
|
46
|
+
}, [theme])
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** A screen with a large title that collapses into the bar as its list scrolls. */
|
|
50
|
+
export const LARGE_TITLE_SCREEN_OPTIONS: StackScreenOptions = {headerLargeTitleEnabled: true, headerShown: true}
|
|
51
|
+
|
|
52
|
+
/** A screen without the stack's header, for one that draws its own or none. */
|
|
53
|
+
export const HIDDEN_HEADER_SCREEN_OPTIONS: StackScreenOptions = {headerShown: false}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* A screen whose content runs under a transparent bar, a full-bleed image above all, for `useHeroHeader`. Static, so the
|
|
57
|
+
* first frame already has no large title and no bar background over the image.
|
|
58
|
+
*/
|
|
59
|
+
export const HERO_HEADER_SCREEN_OPTIONS: StackScreenOptions = {
|
|
60
|
+
headerBlurEffect: 'none',
|
|
61
|
+
headerLargeTitleEnabled: false,
|
|
62
|
+
headerShown: true,
|
|
63
|
+
headerStyle: {backgroundColor: 'transparent'},
|
|
64
|
+
headerTransparent: true,
|
|
65
|
+
scrollEdgeEffects: {top: 'hidden'},
|
|
66
|
+
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {Text as ComposeText, DropdownMenu, DropdownMenuItem} from '@expo/ui/jetpack-compose'
|
|
2
2
|
import {useAppTheme} from '@hzblj/kara-theme'
|
|
3
3
|
import {type ReactNode, useCallback, useState} from 'react'
|
|
4
|
+
import {Glyph} from '../../atoms/glyph'
|
|
4
5
|
import {HStack} from '../../atoms/h-stack'
|
|
5
6
|
import {Icon} from '../../atoms/icon'
|
|
6
7
|
import {IconButton} from '../../atoms/icon-button'
|
|
7
8
|
import {Text} from '../../atoms/text'
|
|
8
|
-
import {
|
|
9
|
-
import {useHostGuard} from '../../host-context'
|
|
9
|
+
import {useHostGuard} from '../../hooks/host-context'
|
|
10
10
|
import type {MenuAction, MenuComponent, MenuProps} from './menu.types'
|
|
11
11
|
import {isIconOnly, menuActionColor} from './menu-parts'
|
|
12
12
|
|