@ng-native/expo 0.12.0 → 0.14.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.
Files changed (88) hide show
  1. package/android/build.gradle +2 -0
  2. package/android/src/main/java/dev/ngnative/expo/SearchFieldModule.kt +111 -0
  3. package/dist/app-info.d.ts +18 -15
  4. package/dist/app-info.js +17 -5
  5. package/dist/app-info.js.map +1 -1
  6. package/dist/apple-sign-in.js +5 -4
  7. package/dist/apple-sign-in.js.map +1 -1
  8. package/dist/assets.js +2 -2
  9. package/dist/assets.js.map +1 -1
  10. package/dist/async-storage.js +4 -1
  11. package/dist/async-storage.js.map +1 -1
  12. package/dist/audio.js +2 -2
  13. package/dist/audio.js.map +1 -1
  14. package/dist/battery.js +4 -1
  15. package/dist/battery.js.map +1 -1
  16. package/dist/biometrics.js +5 -4
  17. package/dist/biometrics.js.map +1 -1
  18. package/dist/brightness.js +7 -2
  19. package/dist/brightness.js.map +1 -1
  20. package/dist/camera.js +4 -1
  21. package/dist/camera.js.map +1 -1
  22. package/dist/clipboard.js +5 -2
  23. package/dist/clipboard.js.map +1 -1
  24. package/dist/database.js +2 -2
  25. package/dist/database.js.map +1 -1
  26. package/dist/expo-ui-components.d.ts +101 -9
  27. package/dist/expo-ui-components.js +305 -43
  28. package/dist/expo-ui-components.js.map +1 -1
  29. package/dist/expo-ui.js +7 -1
  30. package/dist/expo-ui.js.map +1 -1
  31. package/dist/foldable.d.ts +2 -0
  32. package/dist/foldable.js +15 -5
  33. package/dist/foldable.js.map +1 -1
  34. package/dist/fonts.d.ts +2 -0
  35. package/dist/fonts.js +12 -14
  36. package/dist/fonts.js.map +1 -1
  37. package/dist/haptics.js +2 -2
  38. package/dist/haptics.js.map +1 -1
  39. package/dist/index.d.ts +1 -1
  40. package/dist/index.js +1 -1
  41. package/dist/index.js.map +1 -1
  42. package/dist/language-model.js +7 -3
  43. package/dist/language-model.js.map +1 -1
  44. package/dist/native.d.ts +17 -2
  45. package/dist/native.js +61 -3
  46. package/dist/native.js.map +1 -1
  47. package/dist/network.js +4 -1
  48. package/dist/network.js.map +1 -1
  49. package/dist/notifications.js +35 -8
  50. package/dist/notifications.js.map +1 -1
  51. package/dist/observed.js +5 -1
  52. package/dist/observed.js.map +1 -1
  53. package/dist/orientation.js +4 -1
  54. package/dist/orientation.js.map +1 -1
  55. package/dist/register-expo-view.d.ts +19 -0
  56. package/dist/register-expo-view.js +1 -0
  57. package/dist/register-expo-view.js.map +1 -1
  58. package/dist/screen-capture.js +2 -2
  59. package/dist/screen-capture.js.map +1 -1
  60. package/dist/search-field.d.ts +83 -0
  61. package/dist/search-field.js +147 -0
  62. package/dist/search-field.js.map +1 -0
  63. package/dist/secure-store.js +4 -1
  64. package/dist/secure-store.js.map +1 -1
  65. package/dist/sensors.js +6 -3
  66. package/dist/sensors.js.map +1 -1
  67. package/dist/splash-screen.js +2 -2
  68. package/dist/splash-screen.js.map +1 -1
  69. package/dist/store.js +4 -0
  70. package/dist/store.js.map +1 -1
  71. package/dist/tracking.js +2 -2
  72. package/dist/tracking.js.map +1 -1
  73. package/dist/ui-font-names.d.ts +16 -0
  74. package/dist/ui-font-names.js +71 -0
  75. package/dist/ui-font-names.js.map +1 -0
  76. package/dist/updates.js +4 -1
  77. package/dist/updates.js.map +1 -1
  78. package/dist/video.js +2 -2
  79. package/dist/video.js.map +1 -1
  80. package/dist/watch.js +6 -5
  81. package/dist/watch.js.map +1 -1
  82. package/expo-module.config.json +5 -2
  83. package/ios/ExpoUIModifiersModule.swift +25 -0
  84. package/ios/InlineNavigationTitle.swift +12 -0
  85. package/ios/NgNativeExpo.podspec +3 -0
  86. package/ios/SearchField.swift +130 -0
  87. package/ios/SegmentedFont.swift +113 -0
  88. package/package.json +6 -2
@@ -517,6 +517,10 @@ export class UiDatePicker {
517
517
  ...(ngDevMode ? [undefined, { debugName: "title" }] : /* istanbul ignore next */ []));
518
518
  selection = input(/* @ts-ignore */
519
519
  ...(ngDevMode ? [undefined, { debugName: "selection" }] : /* istanbul ignore next */ []));
520
+ /**
521
+ * What the picker shows. An empty list shows nothing in SwiftUI; Compose has no such choice,
522
+ * so on Android it shows the date and the time.
523
+ */
520
524
  displayedComponents = input(/* @ts-ignore */
521
525
  ...(ngDevMode ? [undefined, { debugName: "displayedComponents" }] : /* istanbul ignore next */ []));
522
526
  range = input(/* @ts-ignore */
@@ -535,6 +539,14 @@ export class UiDatePicker {
535
539
  android = nativePlatform() === 'android';
536
540
  resolvedSelection = computed(() => this.android ? undefined : (this.selection() ?? this.value()?.toISOString()), /* @ts-ignore */
537
541
  ...(ngDevMode ? [{ debugName: "resolvedSelection" }] : /* istanbul ignore next */ []));
542
+ /** SwiftUI takes a list of what to show, and Compose one word for it. */
543
+ resolvedComponents = computed(() => {
544
+ const shown = this.displayedComponents();
545
+ if (!this.android || !shown)
546
+ return shown;
547
+ return shown.length === 1 ? shown[0] : 'dateAndTime';
548
+ }, /* @ts-ignore */
549
+ ...(ngDevMode ? [{ debugName: "resolvedComponents" }] : /* istanbul ignore next */ []));
538
550
  initialDate = computed(() => this.android ? (this.value()?.getTime() ?? null) : undefined, /* @ts-ignore */
539
551
  ...(ngDevMode ? [{ debugName: "initialDate" }] : /* istanbul ignore next */ []));
540
552
  resolvedModifiers = computed(() => this.android ? this.modifiers() : withDisabled(this.modifiers(), this.disabled()), /* @ts-ignore */
@@ -560,7 +572,7 @@ export class UiDatePicker {
560
572
  });
561
573
  }
562
574
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: UiDatePicker, deps: [], target: i0.ɵɵFactoryTarget.Component });
563
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.2.0", type: UiDatePicker, isStandalone: true, selector: "ui-date-picker", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, selection: { classPropertyName: "selection", publicName: "selection", isSignal: true, isRequired: false, transformFunction: null }, displayedComponents: { classPropertyName: "displayedComponents", publicName: "displayedComponents", isSignal: true, isRequired: false, transformFunction: null }, range: { classPropertyName: "range", publicName: "range", isSignal: true, isRequired: false, transformFunction: null }, accessibilityLabel: { classPropertyName: "accessibilityLabel", publicName: "accessibilityLabel", isSignal: true, isRequired: false, transformFunction: null }, modifiers: { classPropertyName: "modifiers", publicName: "modifiers", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", touch: "touch", dateChange: "dateChange" }, host: { properties: { "title": "title()", "selection": "resolvedSelection()", "initialDate": "initialDate()", "displayedComponents": "displayedComponents()", "range": "range()", "accessibilityLabel": "accessibilityLabel()", "modifiers": "resolvedModifiers()" } }, ngImport: i0, template: '', isInline: true });
575
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.2.0", type: UiDatePicker, isStandalone: true, selector: "ui-date-picker", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, selection: { classPropertyName: "selection", publicName: "selection", isSignal: true, isRequired: false, transformFunction: null }, displayedComponents: { classPropertyName: "displayedComponents", publicName: "displayedComponents", isSignal: true, isRequired: false, transformFunction: null }, range: { classPropertyName: "range", publicName: "range", isSignal: true, isRequired: false, transformFunction: null }, accessibilityLabel: { classPropertyName: "accessibilityLabel", publicName: "accessibilityLabel", isSignal: true, isRequired: false, transformFunction: null }, modifiers: { classPropertyName: "modifiers", publicName: "modifiers", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", touch: "touch", dateChange: "dateChange" }, host: { properties: { "title": "title()", "selection": "resolvedSelection()", "initialDate": "initialDate()", "displayedComponents": "resolvedComponents()", "range": "range()", "accessibilityLabel": "accessibilityLabel()", "modifiers": "resolvedModifiers()" } }, ngImport: i0, template: '', isInline: true });
564
576
  }
565
577
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: UiDatePicker, decorators: [{
566
578
  type: Component,
@@ -571,7 +583,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImpor
571
583
  '[title]': 'title()',
572
584
  '[selection]': 'resolvedSelection()',
573
585
  '[initialDate]': 'initialDate()',
574
- '[displayedComponents]': 'displayedComponents()',
586
+ '[displayedComponents]': 'resolvedComponents()',
575
587
  '[range]': 'range()',
576
588
  '[accessibilityLabel]': 'accessibilityLabel()',
577
589
  '[modifiers]': 'resolvedModifiers()',
@@ -580,11 +592,141 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImpor
580
592
  }], ctorParameters: () => [], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], selection: [{ type: i0.Input, args: [{ isSignal: true, alias: "selection", required: false }] }], displayedComponents: [{ type: i0.Input, args: [{ isSignal: true, alias: "displayedComponents", required: false }] }], range: [{ type: i0.Input, args: [{ isSignal: true, alias: "range", required: false }] }], accessibilityLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "accessibilityLabel", required: false }] }], modifiers: [{ type: i0.Input, args: [{ isSignal: true, alias: "modifiers", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], touch: [{ type: i0.Output, args: ["touch"] }], dateChange: [{ type: i0.Output, args: ["dateChange"] }] } });
581
593
  /** How long after a date picker appears its first report is taken to be about itself. */
582
594
  const APPEARING_MS = 1000;
595
+ /** Compose's `SingleChoiceSegmentedButtonRow`, Android only: a row of `ui-segmented-button`s. */
596
+ export class UiSingleChoiceSegmentedButtonRow {
597
+ nativeView = nativeView();
598
+ modifiers = input(/* @ts-ignore */
599
+ ...(ngDevMode ? [undefined, { debugName: "modifiers" }] : /* istanbul ignore next */ []));
600
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: UiSingleChoiceSegmentedButtonRow, deps: [], target: i0.ɵɵFactoryTarget.Component });
601
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.2.0", type: UiSingleChoiceSegmentedButtonRow, isStandalone: true, selector: "ui-single-choice-segmented-button-row", inputs: { modifiers: { classPropertyName: "modifiers", publicName: "modifiers", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "modifiers": "modifiers()" } }, ngImport: i0, template: '<ng-content />', isInline: true });
602
+ }
603
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: UiSingleChoiceSegmentedButtonRow, decorators: [{
604
+ type: Component,
605
+ args: [{
606
+ selector: 'ui-single-choice-segmented-button-row',
607
+ template: '<ng-content />',
608
+ host: { '[modifiers]': 'modifiers()' },
609
+ }]
610
+ }], propDecorators: { modifiers: [{ type: i0.Input, args: [{ isSignal: true, alias: "modifiers", required: false }] }] } });
611
+ /** Compose's `SegmentedButton`, Android only, its label in a `<ui-slot name="label">`. */
612
+ export class UiSegmentedButton {
613
+ nativeView = nativeView();
614
+ selected = input(false, { ...(ngDevMode ? { debugName: "selected" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
615
+ enabled = input(true, { ...(ngDevMode ? { debugName: "enabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
616
+ modifiers = input(/* @ts-ignore */
617
+ ...(ngDevMode ? [undefined, { debugName: "modifiers" }] : /* istanbul ignore next */ []));
618
+ press = output();
619
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: UiSegmentedButton, deps: [], target: i0.ɵɵFactoryTarget.Component });
620
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.2.0", type: UiSegmentedButton, isStandalone: true, selector: "ui-segmented-button", inputs: { selected: { classPropertyName: "selected", publicName: "selected", isSignal: true, isRequired: false, transformFunction: null }, enabled: { classPropertyName: "enabled", publicName: "enabled", isSignal: true, isRequired: false, transformFunction: null }, modifiers: { classPropertyName: "modifiers", publicName: "modifiers", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { press: "press" }, host: { listeners: { "buttonPressed": "press.emit()" }, properties: { "selected": "selected()", "enabled": "enabled()", "modifiers": "modifiers()" } }, ngImport: i0, template: '<ng-content />', isInline: true });
621
+ }
622
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: UiSegmentedButton, decorators: [{
623
+ type: Component,
624
+ args: [{
625
+ selector: 'ui-segmented-button',
626
+ template: '<ng-content />',
627
+ host: {
628
+ '[selected]': 'selected()',
629
+ '[enabled]': 'enabled()',
630
+ '[modifiers]': 'modifiers()',
631
+ '(buttonPressed)': 'press.emit()',
632
+ },
633
+ }]
634
+ }], propDecorators: { selected: [{ type: i0.Input, args: [{ isSignal: true, alias: "selected", required: false }] }], enabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "enabled", required: false }] }], modifiers: [{ type: i0.Input, args: [{ isSignal: true, alias: "modifiers", required: false }] }], press: [{ type: i0.Output, args: ["press"] }] } });
635
+ /**
636
+ * Compose's `ExposedDropdownMenuBox`, Android only: an anchor, a view with the `menuAnchor`
637
+ * modifier, and the `ui-exposed-dropdown-menu` it opens. A tap on the anchor asks to open or close
638
+ * it, through `toggled`; `expanded` says which it is.
639
+ */
640
+ export class UiExposedDropdownMenuBox {
641
+ nativeView = nativeView();
642
+ expanded = input(false, { ...(ngDevMode ? { debugName: "expanded" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
643
+ toggled = output();
644
+ modifiers = input(/* @ts-ignore */
645
+ ...(ngDevMode ? [undefined, { debugName: "modifiers" }] : /* istanbul ignore next */ []));
646
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: UiExposedDropdownMenuBox, deps: [], target: i0.ɵɵFactoryTarget.Component });
647
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.2.0", type: UiExposedDropdownMenuBox, isStandalone: true, selector: "ui-exposed-dropdown-menu-box", inputs: { expanded: { classPropertyName: "expanded", publicName: "expanded", isSignal: true, isRequired: false, transformFunction: null }, modifiers: { classPropertyName: "modifiers", publicName: "modifiers", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { toggled: "toggled" }, host: { listeners: { "expandedChange": "toggled.emit($event.nativeEvent.value)" }, properties: { "expanded": "expanded()", "modifiers": "modifiers()" } }, ngImport: i0, template: '<ng-content />', isInline: true });
648
+ }
649
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: UiExposedDropdownMenuBox, decorators: [{
650
+ type: Component,
651
+ args: [{
652
+ selector: 'ui-exposed-dropdown-menu-box',
653
+ template: '<ng-content />',
654
+ host: {
655
+ '[expanded]': 'expanded()',
656
+ '[modifiers]': 'modifiers()',
657
+ // Not a model: its output would be named for this event, and take it.
658
+ '(expandedChange)': 'toggled.emit($event.nativeEvent.value)',
659
+ },
660
+ }]
661
+ }], propDecorators: { expanded: [{ type: i0.Input, args: [{ isSignal: true, alias: "expanded", required: false }] }], toggled: [{ type: i0.Output, args: ["toggled"] }], modifiers: [{ type: i0.Input, args: [{ isSignal: true, alias: "modifiers", required: false }] }] } });
662
+ /** Compose's `ExposedDropdownMenu`, Android only: the `ui-dropdown-menu-item`s a box opens. */
663
+ export class UiExposedDropdownMenu {
664
+ nativeView = nativeView();
665
+ expanded = input(false, { ...(ngDevMode ? { debugName: "expanded" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
666
+ modifiers = input(/* @ts-ignore */
667
+ ...(ngDevMode ? [undefined, { debugName: "modifiers" }] : /* istanbul ignore next */ []));
668
+ /** A tap outside the menu, or Back: the box should close it. */
669
+ dismiss = output();
670
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: UiExposedDropdownMenu, deps: [], target: i0.ɵɵFactoryTarget.Component });
671
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.2.0", type: UiExposedDropdownMenu, isStandalone: true, selector: "ui-exposed-dropdown-menu", inputs: { expanded: { classPropertyName: "expanded", publicName: "expanded", isSignal: true, isRequired: false, transformFunction: null }, modifiers: { classPropertyName: "modifiers", publicName: "modifiers", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { dismiss: "dismiss" }, host: { listeners: { "dismissRequest": "dismiss.emit()" }, properties: { "expanded": "expanded()", "modifiers": "modifiers()" } }, ngImport: i0, template: '<ng-content />', isInline: true });
672
+ }
673
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: UiExposedDropdownMenu, decorators: [{
674
+ type: Component,
675
+ args: [{
676
+ selector: 'ui-exposed-dropdown-menu',
677
+ template: '<ng-content />',
678
+ host: {
679
+ '[expanded]': 'expanded()',
680
+ '[modifiers]': 'modifiers()',
681
+ '(dismissRequest)': 'dismiss.emit()',
682
+ },
683
+ }]
684
+ }], propDecorators: { expanded: [{ type: i0.Input, args: [{ isSignal: true, alias: "expanded", required: false }] }], modifiers: [{ type: i0.Input, args: [{ isSignal: true, alias: "modifiers", required: false }] }], dismiss: [{ type: i0.Output, args: ["dismiss"] }] } });
685
+ /** Compose's `DropdownMenuItem`, Android only, its text in a `<ui-slot name="text">`. */
686
+ export class UiDropdownMenuItem {
687
+ nativeView = nativeView();
688
+ enabled = input(true, { ...(ngDevMode ? { debugName: "enabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
689
+ modifiers = input(/* @ts-ignore */
690
+ ...(ngDevMode ? [undefined, { debugName: "modifiers" }] : /* istanbul ignore next */ []));
691
+ press = output();
692
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: UiDropdownMenuItem, deps: [], target: i0.ɵɵFactoryTarget.Component });
693
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.2.0", type: UiDropdownMenuItem, isStandalone: true, selector: "ui-dropdown-menu-item", inputs: { enabled: { classPropertyName: "enabled", publicName: "enabled", isSignal: true, isRequired: false, transformFunction: null }, modifiers: { classPropertyName: "modifiers", publicName: "modifiers", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { press: "press" }, host: { listeners: { "itemPressed": "press.emit()" }, properties: { "enabled": "enabled()", "modifiers": "modifiers()" } }, ngImport: i0, template: '<ng-content />', isInline: true });
694
+ }
695
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: UiDropdownMenuItem, decorators: [{
696
+ type: Component,
697
+ args: [{
698
+ selector: 'ui-dropdown-menu-item',
699
+ template: '<ng-content />',
700
+ host: {
701
+ '[enabled]': 'enabled()',
702
+ '[modifiers]': 'modifiers()',
703
+ '(itemPressed)': 'press.emit()',
704
+ },
705
+ }]
706
+ }], propDecorators: { enabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "enabled", required: false }] }], modifiers: [{ type: i0.Input, args: [{ isSignal: true, alias: "modifiers", required: false }] }], press: [{ type: i0.Output, args: ["press"] }] } });
707
+ /**
708
+ * A segmented picker's `font` modifiers again, as `@ng-native/expo`'s own modifier: SwiftUI draws
709
+ * the segments as a `UISegmentedControl`, which `font` does not reach. In a build without that
710
+ * modifier, Expo Go's, the segments stay in the system font.
711
+ */
712
+ function segmentFonts(modifiers) {
713
+ const segmented = modifiers.some((m) => m.$type === 'pickerStyle' && m['style'] === 'segmented');
714
+ if (!segmented)
715
+ return [];
716
+ return modifiers
717
+ .filter((m) => m.$type === 'font')
718
+ .map((m) => ({ ...m, $type: 'ngNativeSegmentedFont' }));
719
+ }
583
720
  /**
584
- * A SwiftUI `Picker`, iOS only, as a Signal Forms field: `<ui-picker [options]="rooms"
585
- * [formField]="f.room" />`. `options` draws the choices, each tagged with its value, which is the
586
- * content slot and `tag` modifiers `@expo/ui` otherwise wants written out by hand; children still
587
- * work for anything richer. `pickerStyle` is SwiftUI's (`menu`, `segmented`, `wheel`, `inline`).
721
+ * A SwiftUI `Picker` as a Signal Forms field: `<ui-picker [options]="rooms" [formField]="f.room"
722
+ * />`. `options` draws the choices, each tagged with its value, which is the content slot and `tag`
723
+ * modifiers `@expo/ui` otherwise wants written out by hand; children still work for anything
724
+ * richer on iOS. `pickerStyle` is SwiftUI's (`menu`, `segmented`, `wheel`, `inline`).
725
+ *
726
+ * Compose has no `Picker`, so on Android the options are drawn with its own controls: `segmented`
727
+ * as a row of segmented buttons, and any other style as an exposed dropdown menu under the chosen
728
+ * option. `label`, `systemImage`, children and `modifiers` are SwiftUI's alone. A `font` modifier
729
+ * reaches a segmented picker's segments in a development build, not in Expo Go.
588
730
  */
589
731
  export class UiPicker {
590
732
  nativeView = nativeView();
@@ -612,54 +754,146 @@ export class UiPicker {
612
754
  [{ $type: 'tag', tag: option.value }],
613
755
  ])), /* @ts-ignore */
614
756
  ...(ngDevMode ? [{ debugName: "tags" }] : /* istanbul ignore next */ []));
757
+ android = nativePlatform() === 'android';
758
+ /** Whether Android's dropdown is open. */
759
+ open = signal(false, /* @ts-ignore */
760
+ ...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
761
+ /**
762
+ * What Android's dropdown shows closed: the chosen option, or the label before there is one,
763
+ * with the arrow that says a tap opens it, which plain text otherwise does not.
764
+ */
765
+ chosenLabel = computed(() => {
766
+ const chosen = this.options()?.find((option) => option.value === this.value());
767
+ return `${chosen?.label ?? this.label() ?? ''} \u25BE`;
768
+ }, /* @ts-ignore */
769
+ ...(ngDevMode ? [{ debugName: "chosenLabel" }] : /* istanbul ignore next */ []));
770
+ /** What anchors Android's dropdown to the option it shows. */
771
+ anchor = computed(() => [
772
+ { $type: 'menuAnchor', type: 'primaryNotEditable', enabled: !this.disabled() },
773
+ ], /* @ts-ignore */
774
+ ...(ngDevMode ? [{ debugName: "anchor" }] : /* istanbul ignore next */ []));
615
775
  resolvedModifiers = computed(() => {
616
776
  const style = this.pickerStyle();
617
777
  const own = style
618
778
  ? [{ $type: 'pickerStyle', style }, ...(this.modifiers() ?? [])]
619
779
  : this.modifiers();
620
- return withDisabled(own, this.disabled());
780
+ return withDisabled(own && [...own, ...segmentFonts(own)], this.disabled());
621
781
  }, /* @ts-ignore */
622
782
  ...(ngDevMode ? [{ debugName: "resolvedModifiers" }] : /* istanbul ignore next */ []));
623
783
  constructor() {
624
784
  listen('topSelectionChange', ({ selection }) => {
625
785
  if (typeof selection !== 'string' && typeof selection !== 'number')
626
786
  return;
627
- this.value.set(selection);
628
- this.touch.emit();
787
+ this.choose(selection);
629
788
  });
630
789
  }
790
+ choose(selection) {
791
+ this.open.set(false);
792
+ if (this.disabled())
793
+ return;
794
+ this.value.set(selection);
795
+ this.touch.emit();
796
+ }
631
797
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: UiPicker, deps: [], target: i0.ɵɵFactoryTarget.Component });
632
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.2.0", type: UiPicker, isStandalone: true, selector: "ui-picker", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, systemImage: { classPropertyName: "systemImage", publicName: "systemImage", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, pickerStyle: { classPropertyName: "pickerStyle", publicName: "pickerStyle", isSignal: true, isRequired: false, transformFunction: null }, modifiers: { classPropertyName: "modifiers", publicName: "modifiers", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", touch: "touch", selectionChange: "selectionChange" }, host: { properties: { "label": "label()", "systemImage": "systemImage()", "selection": "value()", "modifiers": "resolvedModifiers()" } }, ngImport: i0, template: `
633
- @if (options(); as options) {
634
- <ui-slot name="content">
635
- @for (option of options; track option.value) {
636
- <ui-text [text]="option.label" [modifiers]="tags().get(option.value)" />
637
- }
638
- </ui-slot>
798
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.2.0", type: UiPicker, isStandalone: true, selector: "ui-picker", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, systemImage: { classPropertyName: "systemImage", publicName: "systemImage", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, pickerStyle: { classPropertyName: "pickerStyle", publicName: "pickerStyle", isSignal: true, isRequired: false, transformFunction: null }, modifiers: { classPropertyName: "modifiers", publicName: "modifiers", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", touch: "touch", selectionChange: "selectionChange" }, host: { properties: { "label": "android ? undefined : label()", "systemImage": "android ? undefined : systemImage()", "selection": "android ? undefined : value()", "modifiers": "android ? undefined : resolvedModifiers()" } }, ngImport: i0, template: `
799
+ @if (android) {
800
+ @if (pickerStyle() === 'segmented') {
801
+ <ui-single-choice-segmented-button-row>
802
+ @for (option of options() ?? []; track option.value) {
803
+ <ui-segmented-button
804
+ [selected]="option.value === value()"
805
+ [enabled]="!disabled()"
806
+ (press)="choose(option.value)"
807
+ >
808
+ <ui-slot name="label"><ui-text [text]="option.label" /></ui-slot>
809
+ </ui-segmented-button>
810
+ }
811
+ </ui-single-choice-segmented-button-row>
812
+ } @else {
813
+ <ui-exposed-dropdown-menu-box
814
+ [expanded]="open()"
815
+ (toggled)="open.set(!disabled() && $event)"
816
+ >
817
+ <ui-text [text]="chosenLabel()" [modifiers]="anchor()" />
818
+ <ui-exposed-dropdown-menu [expanded]="open()" (dismiss)="open.set(false)">
819
+ @for (option of options() ?? []; track option.value) {
820
+ <ui-dropdown-menu-item [enabled]="!disabled()" (press)="choose(option.value)">
821
+ <ui-slot name="text"><ui-text [text]="option.label" /></ui-slot>
822
+ </ui-dropdown-menu-item>
823
+ }
824
+ </ui-exposed-dropdown-menu>
825
+ </ui-exposed-dropdown-menu-box>
826
+ }
827
+ } @else {
828
+ @if (options(); as options) {
829
+ <ui-slot name="content">
830
+ @for (option of options; track option.value) {
831
+ <ui-text [text]="option.label" [modifiers]="tags().get(option.value)" />
832
+ }
833
+ </ui-slot>
834
+ }
835
+ <ng-content />
639
836
  }
640
- <ng-content />
641
- `, isInline: true, dependencies: [{ kind: "component", type: UiSlot, selector: "ui-slot", inputs: ["name", "extraProps"] }, { kind: "component", type: UiText, selector: "ui-text", inputs: ["text", "date", "dateStyle", "timerInterval", "countsDown", "modifiers"] }] });
837
+ `, isInline: true, dependencies: [{ kind: "component", type: UiDropdownMenuItem, selector: "ui-dropdown-menu-item", inputs: ["enabled", "modifiers"], outputs: ["press"] }, { kind: "component", type: UiExposedDropdownMenu, selector: "ui-exposed-dropdown-menu", inputs: ["expanded", "modifiers"], outputs: ["dismiss"] }, { kind: "component", type: UiExposedDropdownMenuBox, selector: "ui-exposed-dropdown-menu-box", inputs: ["expanded", "modifiers"], outputs: ["toggled"] }, { kind: "component", type: UiSegmentedButton, selector: "ui-segmented-button", inputs: ["selected", "enabled", "modifiers"], outputs: ["press"] }, { kind: "component", type: UiSingleChoiceSegmentedButtonRow, selector: "ui-single-choice-segmented-button-row", inputs: ["modifiers"] }, { kind: "component", type: UiSlot, selector: "ui-slot", inputs: ["name", "extraProps"] }, { kind: "component", type: UiText, selector: "ui-text", inputs: ["text", "date", "dateStyle", "timerInterval", "countsDown", "modifiers"] }] });
642
838
  }
643
839
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: UiPicker, decorators: [{
644
840
  type: Component,
645
841
  args: [{
646
842
  selector: 'ui-picker',
647
- imports: [UiSlot, UiText],
843
+ imports: [
844
+ UiDropdownMenuItem,
845
+ UiExposedDropdownMenu,
846
+ UiExposedDropdownMenuBox,
847
+ UiSegmentedButton,
848
+ UiSingleChoiceSegmentedButtonRow,
849
+ UiSlot,
850
+ UiText,
851
+ ],
648
852
  template: `
649
- @if (options(); as options) {
650
- <ui-slot name="content">
651
- @for (option of options; track option.value) {
652
- <ui-text [text]="option.label" [modifiers]="tags().get(option.value)" />
653
- }
654
- </ui-slot>
853
+ @if (android) {
854
+ @if (pickerStyle() === 'segmented') {
855
+ <ui-single-choice-segmented-button-row>
856
+ @for (option of options() ?? []; track option.value) {
857
+ <ui-segmented-button
858
+ [selected]="option.value === value()"
859
+ [enabled]="!disabled()"
860
+ (press)="choose(option.value)"
861
+ >
862
+ <ui-slot name="label"><ui-text [text]="option.label" /></ui-slot>
863
+ </ui-segmented-button>
864
+ }
865
+ </ui-single-choice-segmented-button-row>
866
+ } @else {
867
+ <ui-exposed-dropdown-menu-box
868
+ [expanded]="open()"
869
+ (toggled)="open.set(!disabled() && $event)"
870
+ >
871
+ <ui-text [text]="chosenLabel()" [modifiers]="anchor()" />
872
+ <ui-exposed-dropdown-menu [expanded]="open()" (dismiss)="open.set(false)">
873
+ @for (option of options() ?? []; track option.value) {
874
+ <ui-dropdown-menu-item [enabled]="!disabled()" (press)="choose(option.value)">
875
+ <ui-slot name="text"><ui-text [text]="option.label" /></ui-slot>
876
+ </ui-dropdown-menu-item>
877
+ }
878
+ </ui-exposed-dropdown-menu>
879
+ </ui-exposed-dropdown-menu-box>
880
+ }
881
+ } @else {
882
+ @if (options(); as options) {
883
+ <ui-slot name="content">
884
+ @for (option of options; track option.value) {
885
+ <ui-text [text]="option.label" [modifiers]="tags().get(option.value)" />
886
+ }
887
+ </ui-slot>
888
+ }
889
+ <ng-content />
655
890
  }
656
- <ng-content />
657
891
  `,
658
892
  host: {
659
- '[label]': 'label()',
660
- '[systemImage]': 'systemImage()',
661
- '[selection]': 'value()',
662
- '[modifiers]': 'resolvedModifiers()',
893
+ '[label]': 'android ? undefined : label()',
894
+ '[systemImage]': 'android ? undefined : systemImage()',
895
+ '[selection]': 'android ? undefined : value()',
896
+ '[modifiers]': 'android ? undefined : resolvedModifiers()',
663
897
  },
664
898
  }]
665
899
  }], ctorParameters: () => [], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], systemImage: [{ type: i0.Input, args: [{ isSignal: true, alias: "systemImage", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], pickerStyle: [{ type: i0.Input, args: [{ isSignal: true, alias: "pickerStyle", required: false }] }], modifiers: [{ type: i0.Input, args: [{ isSignal: true, alias: "modifiers", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], touch: [{ type: i0.Output, args: ["touch"] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }] } });
@@ -1382,8 +1616,9 @@ function windowWidth() {
1382
1616
  * Unlike the other components here, the element this is mounted on is a plain view, which takes
1383
1617
  * no room and needs no `ui-host` around it. The sheet is inside it, wrapped as `@expo/ui`'s own
1384
1618
  * React component wraps one: a `ui-host`, the sheet, on iOS a `ui-group` that carries the
1385
- * presentation modifiers, and a `ui-view-host` holding the one view the content goes in. With
1386
- * toolbar items, the view host is in a `ui-toolbar` in a `ui-navigation-stack`, under the group.
1619
+ * presentation modifiers, and a `ui-view-host` holding the one view the content goes in. Where the
1620
+ * build has SwiftUI's toolbar and the sheet is not sized to its content, the view host is in a
1621
+ * `ui-navigation-stack` under the group, over a `ui-toolbar` that holds the items.
1387
1622
  */
1388
1623
  export class UiBottomSheet {
1389
1624
  /**
@@ -1443,6 +1678,14 @@ export class UiBottomSheet {
1443
1678
  /** Whether the sheet has a toolbar: it has items for one, and the build has the views. */
1444
1679
  toolbar = computed(() => this.toolbarInBuild && this.toolbarItems().length > 0, /* @ts-ignore */
1445
1680
  ...(ngDevMode ? [{ debugName: "toolbar" }] : /* istanbul ignore next */ []));
1681
+ /**
1682
+ * A sheet's bar never shows a large title, but a navigation stack leaves room for one under its
1683
+ * bar. `@expo/ui` has no modifier for the title's display mode, so `@ng-native/expo` registers
1684
+ * one natively, in a build with `@expo/ui`. One without it, Expo Go's, ignores the modifier.
1685
+ */
1686
+ inlineTitle = [
1687
+ { $type: 'ngNativeInlineNavigationTitle' },
1688
+ ];
1446
1689
  /** SwiftUI's sheet stays in the tree; anywhere else it is there while it shows. */
1447
1690
  swiftUI = nativePlatform() === 'ios';
1448
1691
  width = windowWidth();
@@ -1456,8 +1699,8 @@ export class UiBottomSheet {
1456
1699
  * Whether the sheet is sized to its content, as it opened: a view host takes its sizing once,
1457
1700
  * as it mounts. SwiftUI's sheet is not where it has detents to rest at, nor where it has a
1458
1701
  * toolbar: a navigation stack fills what it is given, and the bar is no part of the content.
1459
- * A toolbar that comes or goes while the sheet is open is followed, since the view host is made
1460
- * again around it.
1702
+ * A toolbar that comes or goes while such a sheet is open is followed, since the view host is
1703
+ * made again around it.
1461
1704
  */
1462
1705
  fits = computed(() => {
1463
1706
  this.mounted();
@@ -1465,6 +1708,17 @@ export class UiBottomSheet {
1465
1708
  return untracked(() => this.fitToContents() && !(this.swiftUI && (this.detents()?.length || toolbar)));
1466
1709
  }, /* @ts-ignore */
1467
1710
  ...(ngDevMode ? [{ debugName: "fits" }] : /* istanbul ignore next */ []));
1711
+ /**
1712
+ * Whether the content is in a navigation stack, beside a toolbar: wherever the build has one and
1713
+ * the sheet is not sized to its content, items or none, so that an item coming or going never
1714
+ * makes the view host again. `@expo/ui`'s view host takes its content's touches as it appears and
1715
+ * gives them up as it disappears, and one made again around the same content appears before the
1716
+ * old one has gone, leaving the content none. A stack with no item draws no bar. The toolbar is
1717
+ * beside the view host rather than around it, since SwiftUI makes the view a toolbar is on again
1718
+ * as its items change.
1719
+ */
1720
+ stacked = computed(() => this.toolbarInBuild && !this.fits(), /* @ts-ignore */
1721
+ ...(ngDevMode ? [{ debugName: "stacked" }] : /* istanbul ignore next */ []));
1468
1722
  /**
1469
1723
  * SwiftUI reads how a sheet is presented from modifiers on its content, not from props. The
1470
1724
  * detents of a sheet not sized to its content are followed while it is open.
@@ -1601,14 +1855,17 @@ export class UiBottomSheet {
1601
1855
  >
1602
1856
  @if (mounted()) {
1603
1857
  <ui-group [modifiers]="presentation()">
1604
- @if (toolbar()) {
1858
+ @if (stacked()) {
1605
1859
  <ui-navigation-stack>
1606
- <ui-toolbar>
1860
+ <ui-zstack>
1861
+ <ui-toolbar [modifiers]="inlineTitle">
1862
+ <ui-spacer />
1863
+ <ui-slot name="content"><ng-content select="ui-toolbar-item" /></ui-slot>
1864
+ </ui-toolbar>
1607
1865
  <ui-view-host layoutRoot [matchContents]="fits() || undefined">
1608
1866
  <ui-bottom-sheet-content [style]="body()" [content]="content" />
1609
1867
  </ui-view-host>
1610
- <ui-slot name="content"><ng-content select="ui-toolbar-item" /></ui-slot>
1611
- </ui-toolbar>
1868
+ </ui-zstack>
1612
1869
  </ui-navigation-stack>
1613
1870
  } @else {
1614
1871
  <ui-view-host layoutRoot [matchContents]="fits() || undefined">
@@ -1633,7 +1890,7 @@ export class UiBottomSheet {
1633
1890
  </ui-bottom-sheet-view>
1634
1891
  </ui-host>
1635
1892
  }
1636
- `, isInline: true, dependencies: [{ kind: "component", type: UiBottomSheetContent, selector: "ui-bottom-sheet-content", inputs: ["content"] }, { kind: "component", type: UiBottomSheetView, selector: "ui-bottom-sheet-view", inputs: ["isPresented", "fitToContents", "showDragHandle", "skipPartiallyExpanded"], outputs: ["isPresentedChange", "dismiss", "dismissRequest"] }, { kind: "component", type: UiGroup, selector: "ui-group", inputs: ["modifiers"] }, { kind: "component", type: UiHost, selector: "ui-host", inputs: ["matchContents", "ignoreSafeArea", "useViewportSizeMeasurement"] }, { kind: "component", type: UiNavigationStack, selector: "ui-navigation-stack", inputs: ["modifiers"] }, { kind: "component", type: UiSlot, selector: "ui-slot", inputs: ["name", "extraProps"] }, { kind: "component", type: UiToolbar, selector: "ui-toolbar", inputs: ["modifiers"] }, { kind: "component", type: UiViewHost, selector: "ui-view-host", inputs: ["matchContents", "layoutRoot", "modifiers"] }] });
1893
+ `, isInline: true, dependencies: [{ kind: "component", type: UiBottomSheetContent, selector: "ui-bottom-sheet-content", inputs: ["content"] }, { kind: "component", type: UiBottomSheetView, selector: "ui-bottom-sheet-view", inputs: ["isPresented", "fitToContents", "showDragHandle", "skipPartiallyExpanded"], outputs: ["isPresentedChange", "dismiss", "dismissRequest"] }, { kind: "component", type: UiGroup, selector: "ui-group", inputs: ["modifiers"] }, { kind: "component", type: UiHost, selector: "ui-host", inputs: ["matchContents", "ignoreSafeArea", "useViewportSizeMeasurement"] }, { kind: "component", type: UiNavigationStack, selector: "ui-navigation-stack", inputs: ["modifiers"] }, { kind: "component", type: UiSlot, selector: "ui-slot", inputs: ["name", "extraProps"] }, { kind: "component", type: UiSpacer, selector: "ui-spacer", inputs: ["modifiers"] }, { kind: "component", type: UiToolbar, selector: "ui-toolbar", inputs: ["modifiers"] }, { kind: "component", type: UiViewHost, selector: "ui-view-host", inputs: ["matchContents", "layoutRoot", "modifiers"] }, { kind: "component", type: UiZStack, selector: "ui-zstack", inputs: ["alignment", "modifiers"] }] });
1637
1894
  }
1638
1895
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: UiBottomSheet, decorators: [{
1639
1896
  type: Component,
@@ -1646,8 +1903,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImpor
1646
1903
  UiHost,
1647
1904
  UiNavigationStack,
1648
1905
  UiSlot,
1906
+ UiSpacer,
1649
1907
  UiToolbar,
1650
1908
  UiViewHost,
1909
+ UiZStack,
1651
1910
  ],
1652
1911
  template: `
1653
1912
  <ng-template #content><ng-content /></ng-template>
@@ -1662,14 +1921,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImpor
1662
1921
  >
1663
1922
  @if (mounted()) {
1664
1923
  <ui-group [modifiers]="presentation()">
1665
- @if (toolbar()) {
1924
+ @if (stacked()) {
1666
1925
  <ui-navigation-stack>
1667
- <ui-toolbar>
1926
+ <ui-zstack>
1927
+ <ui-toolbar [modifiers]="inlineTitle">
1928
+ <ui-spacer />
1929
+ <ui-slot name="content"><ng-content select="ui-toolbar-item" /></ui-slot>
1930
+ </ui-toolbar>
1668
1931
  <ui-view-host layoutRoot [matchContents]="fits() || undefined">
1669
1932
  <ui-bottom-sheet-content [style]="body()" [content]="content" />
1670
1933
  </ui-view-host>
1671
- <ui-slot name="content"><ng-content select="ui-toolbar-item" /></ui-slot>
1672
- </ui-toolbar>
1934
+ </ui-zstack>
1673
1935
  </ui-navigation-stack>
1674
1936
  } @else {
1675
1937
  <ui-view-host layoutRoot [matchContents]="fits() || undefined">