@ng-native/expo 0.14.0 → 0.16.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/android/build.gradle +2 -0
- package/android/src/main/java/dev/ngnative/expo/SearchFieldModule.kt +61 -0
- package/dist/camera.d.ts +28 -2
- package/dist/camera.js +26 -2
- package/dist/camera.js.map +1 -1
- package/dist/expo-ui-components.d.ts +73 -6
- package/dist/expo-ui-components.js +202 -54
- package/dist/expo-ui-components.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/player.d.ts +9 -2
- package/dist/player.js +3 -2
- package/dist/player.js.map +1 -1
- package/dist/register-expo-view.d.ts +7 -3
- package/dist/register-expo-view.js +27 -7
- package/dist/register-expo-view.js.map +1 -1
- package/dist/search-field.d.ts +2 -0
- package/dist/search-field.js +15 -2
- package/dist/search-field.js.map +1 -1
- package/dist/ui-bottom-sheet-view.d.ts +3 -1
- package/dist/ui-bottom-sheet-view.js +6 -2
- package/dist/ui-bottom-sheet-view.js.map +1 -1
- package/dist/video.js +1 -1
- package/dist/video.js.map +1 -1
- package/ios/SearchField.swift +70 -0
- package/package.json +2 -2
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,QAAQ,EAAiB,MAAM,eAAe,CAAC;AACxD,OAAO,EACL,UAAU,GAIX,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,kBAAkB,EAClB,UAAU,EACV,QAAQ,EACR,WAAW,GAEZ,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AACzE,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,WAAW,EAAoB,MAAM,mBAAmB,CAAC;AAClE,OAAO,EACL,UAAU,EACV,YAAY,EACZ,gBAAgB,EAChB,iBAAiB,GAElB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,2BAA2B,EAC3B,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,SAAS,EACT,OAAO,EACP,MAAM,EACN,WAAW,EACX,kBAAkB,EAClB,wBAAwB,EACxB,QAAQ,EAER,YAAY,EACZ,SAAS,EACT,MAAM,EACN,OAAO,EACP,MAAM,EACN,QAAQ,EACR,kBAAkB,EAClB,qBAAqB,EACrB,wBAAwB,EACxB,iBAAiB,EACjB,gCAAgC,EAChC,aAAa,EAEb,MAAM,EACN,OAAO,EAWP,OAAO,EACP,QAAQ,EACR,gBAAgB,EAChB,MAAM,EACN,UAAU,EACV,SAAS,EACT,QAAQ,EACR,SAAS,EAET,WAAW,EAEX,QAAQ,EAER,QAAQ,EAER,MAAM,EACN,cAAc,EACd,MAAM,EACN,QAAQ,EAMR,aAAa,EAGb,OAAO,EACP,iBAAiB,EACjB,SAAS,EACT,aAAa,GAEd,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,SAAS,EAAkD,MAAM,iBAAiB,CAAC;AAC5F,OAAO,EACL,SAAS,EACT,kBAAkB,EAClB,oBAAoB,GAGrB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,UAAU,GAMX,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,cAAc,EAAkC,MAAM,uBAAuB,CAAC;AACvF,OAAO,EACL,gBAAgB,GAGjB,MAAM,wBAAwB,CAAC","sourcesContent":["/**\n * What the package exports without naming a module.\n *\n * Everything with an Expo module behind it has its own entry point - `@ng-native/expo/\n * battery.ts` and friends - so that importing haptics does not make an app install the video\n * player. What is left here is the shared machinery: the permission shape every module spells\n * identically, the signal-from-a-listener those services are built out of, and the view names.\n */\nexport { observed, type Observed } from './observed.ts';\nexport {\n Permission,\n type ForegroundEvents,\n type PermissionApi,\n type PermissionResponse,\n} from './permissions.ts';\nexport {\n MissingModuleError,\n expoModule,\n optional,\n unavailable,\n type ModulePlatform,\n} from './native.ts';\nexport { NATIVE_VIEWS, registerNativeViews } from './community-views.ts';\nexport { registerExpoUiViews } from './expo-ui.ts';\nexport { nativeState, type NativeState } from './native-state.ts';\nexport {\n EXPO_VIEWS,\n expoViewName,\n registerExpoView,\n registerExpoViews,\n type ExpoViewOptions,\n} from './register-expo-view.ts';\nexport {\n UiAccessoryWidgetBackground,\n UiButton,\n UiCapsule,\n UiCircle,\n UiEllipse,\n UiLabel,\n UiLink,\n UiRectangle,\n UiRoundedRectangle,\n UiUnevenRoundedRectangle,\n UiZStack,\n type UiZStackAlignment,\n UiDatePicker,\n UiDivider,\n UiHost,\n UiImage,\n UiMenu,\n UiPicker,\n UiDropdownMenuItem,\n UiExposedDropdownMenu,\n UiExposedDropdownMenuBox,\n UiSegmentedButton,\n UiSingleChoiceSegmentedButtonRow,\n UiColorPicker,\n type UiColorChangeEvent,\n UiForm,\n UiChart,\n type UiChartAreaStyle,\n type UiChartBarStyle,\n type UiChartDataPoint,\n type UiChartLineStyle,\n type UiChartPieStyle,\n type UiChartPointStyle,\n type UiChartPointSymbol,\n type UiChartRectangleStyle,\n type UiChartRuleStyle,\n type UiChartType,\n UiGauge,\n UiHStack,\n UiLabeledContent,\n UiList,\n UiProgress,\n UiSection,\n UiSpacer,\n UiStepper,\n type UiStepperChangeEvent,\n UiTextField,\n type UiTextFieldChangeEvent,\n UiToggle,\n type UiToggleChangeEvent,\n UiSlider,\n type UiSliderChangeEvent,\n UiSlot,\n UiSwipeActions,\n UiText,\n UiVStack,\n type UiDate,\n type UiDateChangeEvent,\n type UiTimerInterval,\n type UiPickerOption,\n type UiModifier,\n UiBottomSheet,\n type UiBottomSheetViewFunctions,\n type UiPresentationDetent,\n UiGroup,\n UiNavigationStack,\n UiToolbar,\n UiToolbarItem,\n type UiToolbarItemPlacement,\n} from './expo-ui-components.ts';\nexport { ExpoImage, type ExpoImageContentFit, type ExpoImageSource } from './expo-image.ts';\nexport {\n ExpoGlass,\n ExpoGlassContainer,\n liquidGlassAvailable,\n type GlassEffectStyleConfig,\n type GlassStyle,\n} from './glass.ts';\nexport {\n ExpoSymbol,\n type SymbolAnimationSpec,\n type SymbolResizeMode,\n type SymbolScale,\n type SymbolType,\n type SymbolWeight,\n} from './symbol.ts';\nexport { DateTimePicker, type DateTimePickerChangeEvent } from './date-time-picker.ts';\nexport {\n SegmentedControl,\n type SegmentedControlChangeEvent,\n type SegmentedControlFont,\n} from './segmented-control.ts';\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,QAAQ,EAAiB,MAAM,eAAe,CAAC;AACxD,OAAO,EACL,UAAU,GAIX,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,kBAAkB,EAClB,UAAU,EACV,QAAQ,EACR,WAAW,GAEZ,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AACzE,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,WAAW,EAAoB,MAAM,mBAAmB,CAAC;AAClE,OAAO,EACL,UAAU,EACV,YAAY,EACZ,gBAAgB,EAChB,iBAAiB,GAElB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,2BAA2B,EAC3B,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,aAAa,EACb,SAAS,EACT,OAAO,EACP,MAAM,EACN,WAAW,EACX,kBAAkB,EAClB,wBAAwB,EACxB,UAAU,EACV,QAAQ,EAER,YAAY,EACZ,SAAS,EACT,MAAM,EACN,OAAO,EACP,MAAM,EACN,QAAQ,EACR,kBAAkB,EAClB,qBAAqB,EACrB,wBAAwB,EACxB,iBAAiB,EACjB,gCAAgC,EAChC,aAAa,EAEb,MAAM,EACN,OAAO,EAWP,OAAO,EACP,QAAQ,EACR,gBAAgB,EAChB,MAAM,EACN,UAAU,EACV,SAAS,EACT,QAAQ,EACR,SAAS,EAET,WAAW,EAEX,QAAQ,EAER,QAAQ,EAER,MAAM,EACN,cAAc,EACd,MAAM,EACN,QAAQ,EAMR,aAAa,EAGb,OAAO,EACP,iBAAiB,EACjB,SAAS,EACT,aAAa,GAEd,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,SAAS,EAAkD,MAAM,iBAAiB,CAAC;AAC5F,OAAO,EACL,SAAS,EACT,kBAAkB,EAClB,oBAAoB,GAGrB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,UAAU,GAMX,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,cAAc,EAAkC,MAAM,uBAAuB,CAAC;AACvF,OAAO,EACL,gBAAgB,GAGjB,MAAM,wBAAwB,CAAC","sourcesContent":["/**\n * What the package exports without naming a module.\n *\n * Everything with an Expo module behind it has its own entry point - `@ng-native/expo/\n * battery.ts` and friends - so that importing haptics does not make an app install the video\n * player. What is left here is the shared machinery: the permission shape every module spells\n * identically, the signal-from-a-listener those services are built out of, and the view names.\n */\nexport { observed, type Observed } from './observed.ts';\nexport {\n Permission,\n type ForegroundEvents,\n type PermissionApi,\n type PermissionResponse,\n} from './permissions.ts';\nexport {\n MissingModuleError,\n expoModule,\n optional,\n unavailable,\n type ModulePlatform,\n} from './native.ts';\nexport { NATIVE_VIEWS, registerNativeViews } from './community-views.ts';\nexport { registerExpoUiViews } from './expo-ui.ts';\nexport { nativeState, type NativeState } from './native-state.ts';\nexport {\n EXPO_VIEWS,\n expoViewName,\n registerExpoView,\n registerExpoViews,\n type ExpoViewOptions,\n} from './register-expo-view.ts';\nexport {\n UiAccessoryWidgetBackground,\n UiButton,\n UiCapsule,\n UiCircle,\n UiContextMenu,\n UiEllipse,\n UiLabel,\n UiLink,\n UiRectangle,\n UiRoundedRectangle,\n UiUnevenRoundedRectangle,\n UiViewHost,\n UiZStack,\n type UiZStackAlignment,\n UiDatePicker,\n UiDivider,\n UiHost,\n UiImage,\n UiMenu,\n UiPicker,\n UiDropdownMenuItem,\n UiExposedDropdownMenu,\n UiExposedDropdownMenuBox,\n UiSegmentedButton,\n UiSingleChoiceSegmentedButtonRow,\n UiColorPicker,\n type UiColorChangeEvent,\n UiForm,\n UiChart,\n type UiChartAreaStyle,\n type UiChartBarStyle,\n type UiChartDataPoint,\n type UiChartLineStyle,\n type UiChartPieStyle,\n type UiChartPointStyle,\n type UiChartPointSymbol,\n type UiChartRectangleStyle,\n type UiChartRuleStyle,\n type UiChartType,\n UiGauge,\n UiHStack,\n UiLabeledContent,\n UiList,\n UiProgress,\n UiSection,\n UiSpacer,\n UiStepper,\n type UiStepperChangeEvent,\n UiTextField,\n type UiTextFieldChangeEvent,\n UiToggle,\n type UiToggleChangeEvent,\n UiSlider,\n type UiSliderChangeEvent,\n UiSlot,\n UiSwipeActions,\n UiText,\n UiVStack,\n type UiDate,\n type UiDateChangeEvent,\n type UiTimerInterval,\n type UiPickerOption,\n type UiModifier,\n UiBottomSheet,\n type UiBottomSheetViewFunctions,\n type UiPresentationDetent,\n UiGroup,\n UiNavigationStack,\n UiToolbar,\n UiToolbarItem,\n type UiToolbarItemPlacement,\n} from './expo-ui-components.ts';\nexport { ExpoImage, type ExpoImageContentFit, type ExpoImageSource } from './expo-image.ts';\nexport {\n ExpoGlass,\n ExpoGlassContainer,\n liquidGlassAvailable,\n type GlassEffectStyleConfig,\n type GlassStyle,\n} from './glass.ts';\nexport {\n ExpoSymbol,\n type SymbolAnimationSpec,\n type SymbolResizeMode,\n type SymbolScale,\n type SymbolType,\n type SymbolWeight,\n} from './symbol.ts';\nexport { DateTimePicker, type DateTimePickerChangeEvent } from './date-time-picker.ts';\nexport {\n SegmentedControl,\n type SegmentedControlChangeEvent,\n type SegmentedControlFont,\n} from './segmented-control.ts';\n"]}
|
package/dist/player.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* ```ts
|
|
6
6
|
* protected readonly player = videoPlayer(this.source, { timeUpdate: 0.5 });
|
|
7
7
|
*
|
|
8
|
-
* // <expo-video [player]="player.
|
|
8
|
+
* // <expo-video [player]="player.id" />
|
|
9
9
|
* // {{ player.state().currentTime }}
|
|
10
10
|
* ```
|
|
11
11
|
*
|
|
@@ -91,8 +91,15 @@ export declare function watchAudioPlayer(player: NativePlayer | null): {
|
|
|
91
91
|
stop: () => void;
|
|
92
92
|
};
|
|
93
93
|
export interface Player<T> {
|
|
94
|
-
/** The module's own player, for `play()`, `seekBy()
|
|
94
|
+
/** The module's own player, for `play()`, `seekBy()` and the rest. */
|
|
95
95
|
readonly native: T;
|
|
96
|
+
/**
|
|
97
|
+
* What `<expo-video [player]>` takes: the player's shared-object id. The view's `player` prop is
|
|
98
|
+
* a number on the native side, and `expo-video`'s own `VideoView` passes this id rather than the
|
|
99
|
+
* player; given the player itself, the view has no player and draws nothing, while the video
|
|
100
|
+
* keeps playing with nothing on screen.
|
|
101
|
+
*/
|
|
102
|
+
readonly id: number | undefined;
|
|
96
103
|
readonly state: Signal<PlayerState>;
|
|
97
104
|
}
|
|
98
105
|
/**
|
package/dist/player.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* ```ts
|
|
6
6
|
* protected readonly player = videoPlayer(this.source, { timeUpdate: 0.5 });
|
|
7
7
|
*
|
|
8
|
-
* // <expo-video [player]="player.
|
|
8
|
+
* // <expo-video [player]="player.id" />
|
|
9
9
|
* // {{ player.state().currentTime }}
|
|
10
10
|
* ```
|
|
11
11
|
*
|
|
@@ -123,6 +123,7 @@ export function watchAudioPlayer(player) {
|
|
|
123
123
|
export function ownPlayer(native, watch) {
|
|
124
124
|
const { state, stop } = watch();
|
|
125
125
|
inject(DestroyRef).onDestroy(stop);
|
|
126
|
-
|
|
126
|
+
const id = native.__expo_shared_object_id__;
|
|
127
|
+
return { native, id, state };
|
|
127
128
|
}
|
|
128
129
|
//# sourceMappingURL=player.js.map
|
package/dist/player.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"player.js","sourceRoot":"","sources":["../src/player.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * What `videoPlayer` (`@ng-native/expo/video`) and `audioPlayer` (`@ng-native/expo/audio`) share:\n * the `Player` each answers, its state, and the watchers that keep that state.\n *\n * ```ts\n * protected readonly player = videoPlayer(this.source, { timeUpdate: 0.5 });\n *\n * // <expo-video [player]=\"player.native\" />\n * // {{ player.state().currentTime }}\n * ```\n *\n * `useVideoPlayer` and `useAudioPlayer` are the documented way in, and the player they hand back\n * is not the React part - `createVideoPlayer` gives the same object. What the hooks add is the\n * two things a component actually needs and cannot get otherwise: the player is *released* when\n * the component goes, and its changing state is *readable*, because the player's own properties\n * are plain mutable fields that no template can watch.\n *\n * The player itself is passed straight through: it is a shared object with `play()`, `seekBy()`\n * and the rest already on it, and wrapping those would be a second place for each to be wrong.\n *\n * Each player has an entry point of its own because each needs its own native module: Metro fails\n * a build on a `require` it cannot resolve, so one entry point for both made an app install both.\n */\nimport { DestroyRef, inject, signal, type Signal } from '@angular/core';\n\n/** The player's own status, which is the same word in both modules. */\nexport type PlayerStatus = 'idle' | 'loading' | 'readyToPlay' | 'error';\n\n/** The slice of a player this reads. Both modules' players satisfy it structurally. */\nexport interface NativePlayer {\n addListener(event: string, listener: (payload: never) => void): { remove(): void };\n release?(): void;\n /** Seconds between `timeUpdate` events. Zero, its default, means the event never fires. `expo-video` only. */\n timeUpdateEventInterval?: number;\n /** Read directly rather than from an event: `expo-audio`'s status carries no volume field. */\n readonly volume?: number;\n /** `expo-video` only, where an event says it only once it changes. */\n readonly muted?: boolean;\n}\n\n/**\n * `expo-audio`'s one status event, `playbackStatusUpdate`. Unlike `expo-video`, which splits its\n * state across several named events, `AudioPlayer` reports everything - including errors - on a\n * single event, at the interval `updateInterval` was created with.\n */\nexport interface NativeAudioStatus {\n readonly playing: boolean;\n readonly mute: boolean;\n readonly duration: number;\n readonly currentTime: number;\n readonly isLoaded: boolean;\n readonly didJustFinish: boolean;\n readonly error: string | null;\n}\n\nexport interface PlayerState {\n readonly playing: boolean;\n readonly status: PlayerStatus;\n /** Seconds. Only moves while `timeUpdateEventInterval` is non-zero. */\n readonly currentTime: number;\n readonly duration: number;\n readonly muted: boolean;\n readonly volume: number;\n /** Set once playback has run to the end, and cleared when it starts again. */\n readonly ended: boolean;\n}\n\nconst INITIAL: PlayerState = {\n playing: false,\n status: 'idle',\n currentTime: 0,\n duration: 0,\n muted: false,\n volume: 1,\n ended: false,\n};\n\n/**\n * Watch an `expo-video` player, and stop watching when the caller is done with it.\n *\n * `timeUpdate` defaults to off, so `currentTime` would never move. A caller that wants a progress\n * bar asks for one by passing an interval, and one that does not pays nothing: the event is the\n * most frequent thing the player emits.\n *\n * `expo-audio`'s player reports its state differently - one event, not several - so it is watched\n * by `watchAudioPlayer` below rather than this function.\n */\nexport function watchPlayer(\n player: NativePlayer | null,\n options: { timeUpdate?: number } = {},\n): { state: Signal<PlayerState>; stop: () => void } {\n // Both are the player's own, set where it was made: no event says either until it changes.\n const state = signal({\n ...INITIAL,\n muted: player?.muted ?? INITIAL.muted,\n volume: player?.volume ?? INITIAL.volume,\n });\n if (!player) return { state: state.asReadonly(), stop: () => {} };\n\n const patch = (change: Partial<PlayerState>) => state.update((last) => ({ ...last, ...change }));\n const subscriptions = [\n listen(player, 'statusChange', ({ status }: { status: PlayerStatus }) => patch({ status })),\n listen(player, 'playingChange', ({ isPlaying }: { isPlaying: boolean }) =>\n patch({ playing: isPlaying, ...(isPlaying ? { ended: false } : {}) }),\n ),\n listen(player, 'mutedChange', ({ muted }: { muted: boolean }) => patch({ muted })),\n listen(player, 'volumeChange', ({ volume }: { volume: number }) => patch({ volume })),\n listen(player, 'playToEnd', () => patch({ ended: true, playing: false })),\n listen(player, 'timeUpdate', ({ currentTime }: { currentTime: number }) =>\n patch({ currentTime }),\n ),\n // What was the old source's starts over. Muted and volume are the player's, and it keeps them.\n listen(player, 'sourceChange', () =>\n patch({ status: 'loading', playing: false, currentTime: 0, duration: 0, ended: false }),\n ),\n // The only event that says how long the video is: nothing else carries a duration.\n listen(player, 'sourceLoad', ({ duration }: { duration: number }) => patch({ duration })),\n ];\n\n if (options.timeUpdate !== undefined) player.timeUpdateEventInterval = options.timeUpdate;\n\n return {\n state: state.asReadonly(),\n stop: () => {\n for (const subscription of subscriptions) subscription();\n // Releasing is the half a hook does that nothing else will: a player left alive holds its\n // decoder, its audio session and, on iOS, the now-playing controls.\n player.release?.();\n },\n };\n}\n\nfunction listen<T>(player: NativePlayer, event: string, handler: (payload: T) => void): () => void {\n const subscription = player.addListener(event, handler as (payload: never) => void);\n return () => subscription.remove();\n}\n\n/**\n * Watch an `expo-audio` player, and stop watching when the caller is done with it.\n *\n * `AudioPlayer` reports its whole state on `playbackStatusUpdate`, at the interval it was created\n * with, rather than splitting it across the several events `expo-video` uses - so unlike\n * `watchPlayer`, there is one listener, not several, and nothing to reset on a source change: the\n * next status update carries the new source's own state.\n */\nexport function watchAudioPlayer(player: NativePlayer | null): {\n state: Signal<PlayerState>;\n stop: () => void;\n} {\n const state = signal(INITIAL);\n if (!player) return { state: state.asReadonly(), stop: () => {} };\n\n const subscription = listen(player, 'playbackStatusUpdate', (status: NativeAudioStatus) =>\n state.set({\n playing: status.playing,\n status: status.error !== null ? 'error' : status.isLoaded ? 'readyToPlay' : 'loading',\n currentTime: status.currentTime,\n duration: status.duration,\n muted: status.mute,\n // Not on the status event; the player's own property is the current value.\n volume: player.volume ?? INITIAL.volume,\n ended: status.didJustFinish,\n }),\n );\n\n return {\n state: state.asReadonly(),\n stop: () => {\n subscription();\n // Releasing is the half a hook does that nothing else will: a player left alive holds its\n // decoder, its audio session and, on iOS, the now-playing controls.\n player.release?.();\n },\n };\n}\n\nexport interface Player<T> {\n /** The module's own player, for `play()`, `seekBy()`, and to hand to its view. */\n readonly native: T;\n readonly state: Signal<PlayerState>;\n}\n\n/**\n * Tie a player's life to the component that made it.\n *\n * Called in an injection context, which is the same constraint the hooks have and for the same\n * reason: something has to know when the thing goes away.\n */\nexport function ownPlayer<T extends NativePlayer>(\n native: T,\n watch: () => { state: Signal<PlayerState>; stop: () => void },\n): Player<T> {\n const { state, stop } = watch();\n inject(DestroyRef).onDestroy(stop);\n return { native, state };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"player.js","sourceRoot":"","sources":["../src/player.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * What `videoPlayer` (`@ng-native/expo/video`) and `audioPlayer` (`@ng-native/expo/audio`) share:\n * the `Player` each answers, its state, and the watchers that keep that state.\n *\n * ```ts\n * protected readonly player = videoPlayer(this.source, { timeUpdate: 0.5 });\n *\n * // <expo-video [player]=\"player.id\" />\n * // {{ player.state().currentTime }}\n * ```\n *\n * `useVideoPlayer` and `useAudioPlayer` are the documented way in, and the player they hand back\n * is not the React part - `createVideoPlayer` gives the same object. What the hooks add is the\n * two things a component actually needs and cannot get otherwise: the player is *released* when\n * the component goes, and its changing state is *readable*, because the player's own properties\n * are plain mutable fields that no template can watch.\n *\n * The player itself is passed straight through: it is a shared object with `play()`, `seekBy()`\n * and the rest already on it, and wrapping those would be a second place for each to be wrong.\n *\n * Each player has an entry point of its own because each needs its own native module: Metro fails\n * a build on a `require` it cannot resolve, so one entry point for both made an app install both.\n */\nimport { DestroyRef, inject, signal, type Signal } from '@angular/core';\n\n/** The player's own status, which is the same word in both modules. */\nexport type PlayerStatus = 'idle' | 'loading' | 'readyToPlay' | 'error';\n\n/** The slice of a player this reads. Both modules' players satisfy it structurally. */\nexport interface NativePlayer {\n addListener(event: string, listener: (payload: never) => void): { remove(): void };\n release?(): void;\n /** Seconds between `timeUpdate` events. Zero, its default, means the event never fires. `expo-video` only. */\n timeUpdateEventInterval?: number;\n /** Read directly rather than from an event: `expo-audio`'s status carries no volume field. */\n readonly volume?: number;\n /** `expo-video` only, where an event says it only once it changes. */\n readonly muted?: boolean;\n}\n\n/**\n * `expo-audio`'s one status event, `playbackStatusUpdate`. Unlike `expo-video`, which splits its\n * state across several named events, `AudioPlayer` reports everything - including errors - on a\n * single event, at the interval `updateInterval` was created with.\n */\nexport interface NativeAudioStatus {\n readonly playing: boolean;\n readonly mute: boolean;\n readonly duration: number;\n readonly currentTime: number;\n readonly isLoaded: boolean;\n readonly didJustFinish: boolean;\n readonly error: string | null;\n}\n\nexport interface PlayerState {\n readonly playing: boolean;\n readonly status: PlayerStatus;\n /** Seconds. Only moves while `timeUpdateEventInterval` is non-zero. */\n readonly currentTime: number;\n readonly duration: number;\n readonly muted: boolean;\n readonly volume: number;\n /** Set once playback has run to the end, and cleared when it starts again. */\n readonly ended: boolean;\n}\n\nconst INITIAL: PlayerState = {\n playing: false,\n status: 'idle',\n currentTime: 0,\n duration: 0,\n muted: false,\n volume: 1,\n ended: false,\n};\n\n/**\n * Watch an `expo-video` player, and stop watching when the caller is done with it.\n *\n * `timeUpdate` defaults to off, so `currentTime` would never move. A caller that wants a progress\n * bar asks for one by passing an interval, and one that does not pays nothing: the event is the\n * most frequent thing the player emits.\n *\n * `expo-audio`'s player reports its state differently - one event, not several - so it is watched\n * by `watchAudioPlayer` below rather than this function.\n */\nexport function watchPlayer(\n player: NativePlayer | null,\n options: { timeUpdate?: number } = {},\n): { state: Signal<PlayerState>; stop: () => void } {\n // Both are the player's own, set where it was made: no event says either until it changes.\n const state = signal({\n ...INITIAL,\n muted: player?.muted ?? INITIAL.muted,\n volume: player?.volume ?? INITIAL.volume,\n });\n if (!player) return { state: state.asReadonly(), stop: () => {} };\n\n const patch = (change: Partial<PlayerState>) => state.update((last) => ({ ...last, ...change }));\n const subscriptions = [\n listen(player, 'statusChange', ({ status }: { status: PlayerStatus }) => patch({ status })),\n listen(player, 'playingChange', ({ isPlaying }: { isPlaying: boolean }) =>\n patch({ playing: isPlaying, ...(isPlaying ? { ended: false } : {}) }),\n ),\n listen(player, 'mutedChange', ({ muted }: { muted: boolean }) => patch({ muted })),\n listen(player, 'volumeChange', ({ volume }: { volume: number }) => patch({ volume })),\n listen(player, 'playToEnd', () => patch({ ended: true, playing: false })),\n listen(player, 'timeUpdate', ({ currentTime }: { currentTime: number }) =>\n patch({ currentTime }),\n ),\n // What was the old source's starts over. Muted and volume are the player's, and it keeps them.\n listen(player, 'sourceChange', () =>\n patch({ status: 'loading', playing: false, currentTime: 0, duration: 0, ended: false }),\n ),\n // The only event that says how long the video is: nothing else carries a duration.\n listen(player, 'sourceLoad', ({ duration }: { duration: number }) => patch({ duration })),\n ];\n\n if (options.timeUpdate !== undefined) player.timeUpdateEventInterval = options.timeUpdate;\n\n return {\n state: state.asReadonly(),\n stop: () => {\n for (const subscription of subscriptions) subscription();\n // Releasing is the half a hook does that nothing else will: a player left alive holds its\n // decoder, its audio session and, on iOS, the now-playing controls.\n player.release?.();\n },\n };\n}\n\nfunction listen<T>(player: NativePlayer, event: string, handler: (payload: T) => void): () => void {\n const subscription = player.addListener(event, handler as (payload: never) => void);\n return () => subscription.remove();\n}\n\n/**\n * Watch an `expo-audio` player, and stop watching when the caller is done with it.\n *\n * `AudioPlayer` reports its whole state on `playbackStatusUpdate`, at the interval it was created\n * with, rather than splitting it across the several events `expo-video` uses - so unlike\n * `watchPlayer`, there is one listener, not several, and nothing to reset on a source change: the\n * next status update carries the new source's own state.\n */\nexport function watchAudioPlayer(player: NativePlayer | null): {\n state: Signal<PlayerState>;\n stop: () => void;\n} {\n const state = signal(INITIAL);\n if (!player) return { state: state.asReadonly(), stop: () => {} };\n\n const subscription = listen(player, 'playbackStatusUpdate', (status: NativeAudioStatus) =>\n state.set({\n playing: status.playing,\n status: status.error !== null ? 'error' : status.isLoaded ? 'readyToPlay' : 'loading',\n currentTime: status.currentTime,\n duration: status.duration,\n muted: status.mute,\n // Not on the status event; the player's own property is the current value.\n volume: player.volume ?? INITIAL.volume,\n ended: status.didJustFinish,\n }),\n );\n\n return {\n state: state.asReadonly(),\n stop: () => {\n subscription();\n // Releasing is the half a hook does that nothing else will: a player left alive holds its\n // decoder, its audio session and, on iOS, the now-playing controls.\n player.release?.();\n },\n };\n}\n\nexport interface Player<T> {\n /** The module's own player, for `play()`, `seekBy()` and the rest. */\n readonly native: T;\n /**\n * What `<expo-video [player]>` takes: the player's shared-object id. The view's `player` prop is\n * a number on the native side, and `expo-video`'s own `VideoView` passes this id rather than the\n * player; given the player itself, the view has no player and draws nothing, while the video\n * keeps playing with nothing on screen.\n */\n readonly id: number | undefined;\n readonly state: Signal<PlayerState>;\n}\n\n/**\n * Tie a player's life to the component that made it.\n *\n * Called in an injection context, which is the same constraint the hooks have and for the same\n * reason: something has to know when the thing goes away.\n */\nexport function ownPlayer<T extends NativePlayer>(\n native: T,\n watch: () => { state: Signal<PlayerState>; stop: () => void },\n): Player<T> {\n const { state, stop } = watch();\n inject(DestroyRef).onDestroy(stop);\n const id = (native as { __expo_shared_object_id__?: number }).__expo_shared_object_id__;\n return { native, id, state };\n}\n"]}
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* No decorators and no Angular here on purpose: this is a registration, and the components it
|
|
15
15
|
* registers are ordinary elements in a template.
|
|
16
16
|
*/
|
|
17
|
-
import { type ViewNameOptions } from '@ng-native/fabric';
|
|
17
|
+
import { type PlatformViewName, type ViewNameOptions } from '@ng-native/fabric';
|
|
18
18
|
/**
|
|
19
19
|
* The Fabric component name for a module's view, exactly as `requireNativeViewManager` computes
|
|
20
20
|
* it in `expo-modules-core`.
|
|
@@ -29,8 +29,12 @@ import { type ViewNameOptions } from '@ng-native/fabric';
|
|
|
29
29
|
*/
|
|
30
30
|
export declare function expoViewName(moduleName: string, viewName?: string): string;
|
|
31
31
|
export interface ExpoViewOptions {
|
|
32
|
-
/**
|
|
33
|
-
|
|
32
|
+
/**
|
|
33
|
+
* A named view on a module that has several. Omit for the module's default view. A module whose
|
|
34
|
+
* view is named differently on each platform names both: `expo-video`'s is `VideoView` on iOS
|
|
35
|
+
* and `SurfaceVideoView` on Android.
|
|
36
|
+
*/
|
|
37
|
+
readonly viewName?: string | PlatformViewName;
|
|
34
38
|
/**
|
|
35
39
|
* Props the module's React component would have applied before native saw them. Expo's
|
|
36
40
|
* wrappers do less of this than React Native's, but they do some: `expo-image` resolves a
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* registers are ordinary elements in a template.
|
|
16
16
|
*/
|
|
17
17
|
import { registerViewName } from '@ng-native/fabric';
|
|
18
|
-
import { optional } from "./native.js";
|
|
18
|
+
import { currentPlatform, optional } from "./native.js";
|
|
19
19
|
/**
|
|
20
20
|
* The Fabric component name for a module's view, exactly as `requireNativeViewManager` computes
|
|
21
21
|
* it in `expo-modules-core`.
|
|
@@ -42,12 +42,22 @@ export function expoViewName(moduleName, viewName) {
|
|
|
42
42
|
* module installed so the native side registers the component; this is the JavaScript half only.
|
|
43
43
|
*/
|
|
44
44
|
export function registerExpoView(elementName, moduleName, options) {
|
|
45
|
-
|
|
45
|
+
const viewName = options?.viewName;
|
|
46
|
+
registerViewName(elementName, typeof viewName === 'object'
|
|
47
|
+
? {
|
|
48
|
+
ios: expoViewName(moduleName, viewName.ios),
|
|
49
|
+
android: expoViewName(moduleName, viewName.android),
|
|
50
|
+
}
|
|
51
|
+
: expoViewName(moduleName, viewName), options?.defaultProps, {
|
|
46
52
|
textContent: options?.textContent,
|
|
47
53
|
yieldsToComponents: options?.yieldsToComponents,
|
|
48
54
|
fontNames: options?.fontNames,
|
|
49
55
|
});
|
|
50
|
-
optional(() => require('expo').requireNativeView(moduleName,
|
|
56
|
+
optional(() => require('expo').requireNativeView(moduleName, typeof viewName === 'object'
|
|
57
|
+
? currentPlatform() === 'android'
|
|
58
|
+
? viewName.android
|
|
59
|
+
: viewName.ios
|
|
60
|
+
: viewName));
|
|
51
61
|
}
|
|
52
62
|
/**
|
|
53
63
|
* The views worth knowing the names of, so an app does not have to look each one up.
|
|
@@ -63,10 +73,20 @@ export function registerExpoView(elementName, moduleName, options) {
|
|
|
63
73
|
export const EXPO_VIEWS = {
|
|
64
74
|
/** `expo-image`. Takes `source` as a list, so a single `{uri}` is wrapped by the caller. */
|
|
65
75
|
'expo-image': ['ExpoImage', { defaultProps: { contentFit: 'cover' } }],
|
|
66
|
-
/**
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
76
|
+
/**
|
|
77
|
+
* `expo-blur`. The one thing CSS cannot express: a blur of what is *behind* a view. The view is
|
|
78
|
+
* `ExpoBlurView` on the `ExpoBlur` module, as `expo-blur`'s own `NativeBlurModule` asks for it.
|
|
79
|
+
*/
|
|
80
|
+
'expo-blur': [
|
|
81
|
+
'ExpoBlur',
|
|
82
|
+
{ viewName: 'ExpoBlurView', defaultProps: { intensity: 50, tint: 'default' } },
|
|
83
|
+
],
|
|
84
|
+
/**
|
|
85
|
+
* `expo-video`. The player itself is a shared object the module hands out; this is its view,
|
|
86
|
+
* which has another name on Android, as `expo-video`'s own `NativeVideoView` asks for it. Bind
|
|
87
|
+
* `player` to the player's `id` (`videoPlayer`'s), not to the player.
|
|
88
|
+
*/
|
|
89
|
+
'expo-video': ['ExpoVideo', { viewName: { ios: 'VideoView', android: 'SurfaceVideoView' } }],
|
|
70
90
|
/**
|
|
71
91
|
* `expo-camera`. Its module's first view, so its default: `ExpoCamera_CameraView` exists only on
|
|
72
92
|
* iOS, after the Swift class, and commits as nothing on Android. `Camera` takes its pictures.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"register-expo-view.js","sourceRoot":"","sources":["../src/register-expo-view.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Expo's native views, driven directly.\n *\n * An Expo module that renders something registers a Fabric component whose name it derives\n * rather than declares, and `requireNativeViewManager` is the only place that derivation is\n * written down. Everything else that function does is React: it caches a host component, builds\n * a view config for React's renderer, and wraps the result in a class whose whole job is to hold\n * a ref. None of that is reachable without React, and none of it is needed - the engine writes\n * props onto the shadow node itself, and Expo's Fabric views take their props as an untyped map,\n * so there is no codegen'd prop list to satisfy.\n *\n * What is left is the name. That is what this package is.\n *\n * No decorators and no Angular here on purpose: this is a registration, and the components it\n * registers are ordinary elements in a template.\n */\nimport { registerViewName, type ViewNameOptions } from '@ng-native/fabric';\nimport { optional } from './native.ts';\n\n/**\n * What Expo installs on the global when the app starts. Only the app identifier matters here;\n * `getViewConfig` is for React's renderer, which never runs.\n */\ninterface ExpoGlobal {\n readonly __expo_app_identifier__?: string;\n}\n\n/**\n * The Fabric component name for a module's view, exactly as `requireNativeViewManager` computes\n * it in `expo-modules-core`.\n *\n * The app identifier is the part that cannot be hardcoded. Expo Go runs many projects in one\n * binary, so it namespaces every view name with a per-app suffix; a standalone build has no\n * identifier and no suffix. Get it wrong and the view commits as `UnimplementedNativeView`\n * with no error, which is the same failure mode as a misspelled name.\n *\n * A module with more than one view names the extra ones (`viewName`); the default view is the\n * module itself.\n */\nexport function expoViewName(moduleName: string, viewName?: string): string {\n const identifier = (globalThis as { expo?: ExpoGlobal }).expo?.__expo_app_identifier__ ?? '';\n const suffix = identifier ? `_${identifier}` : '';\n const module = viewName ? `${moduleName}_${viewName}` : moduleName;\n return `ViewManagerAdapter_${module}${suffix}`;\n}\n\nexport interface ExpoViewOptions {\n /** A named view on a module that has several. Omit for the module's default view. */\n readonly viewName?: string;\n /**\n * Props the module's React component would have applied before native saw them. Expo's\n * wrappers do less of this than React Native's, but they do some: `expo-image` resolves a\n * source to an array and a `contentFit` to a string native understands.\n */\n readonly defaultProps?: Record<string, unknown>;\n /** Whether a component of the app's own with this selector is a plain view. */\n readonly yieldsToComponents?: boolean;\n /**\n * The prop the view reads its text from, where it takes no child views, as `@expo/ui`'s `Text`\n * reads `text`. The text written inside the element becomes that prop. See `ViewNameOptions`.\n */\n readonly textContent?: string;\n /** Rewrites the fonts the view's own props name. See `ViewNameOptions`. */\n readonly fontNames?: ViewNameOptions['fontNames'];\n}\n\n/**\n * Teach the engine an element name for an Expo module's view, e.g.\n * `registerExpoView('expo-image', 'ExpoImage')` makes `<expo-image>` commit as one.\n *\n * Call it at startup, before the first commit that uses the element. The app must have the\n * module installed so the native side registers the component; this is the JavaScript half only.\n */\nexport function registerExpoView(\n elementName: string,\n moduleName: string,\n options?: ExpoViewOptions,\n): void {\n registerViewName(\n elementName,\n expoViewName(moduleName, options?.viewName),\n options?.defaultProps,\n {\n textContent: options?.textContent,\n yieldsToComponents: options?.yieldsToComponents,\n fontNames: options?.fontNames,\n },\n );\n optional(() =>\n (\n require('expo') as { requireNativeView(module: string, view?: string): unknown }\n ).requireNativeView(moduleName, options?.viewName),\n );\n}\n\n/**\n * The views worth knowing the names of, so an app does not have to look each one up.\n *\n * Deliberately names and defaults, not components. An Angular component per Expo view, prop for\n * prop, is a maintenance burden with no end and a second place for every prop to be wrong; the\n * element plus the module's own documentation covers the common case, and an app that wants a\n * nicer surface writes the one component it actually needs.\n *\n * A module still has to be installed for its element to commit as anything: this is the\n * JavaScript half. An element whose native side is missing commits as `UnimplementedNativeView`.\n */\nexport const EXPO_VIEWS: Readonly<Record<string, [string, ExpoViewOptions?]>> = {\n /** `expo-image`. Takes `source` as a list, so a single `{uri}` is wrapped by the caller. */\n 'expo-image': ['ExpoImage', { defaultProps: { contentFit: 'cover' } }],\n /** `expo-blur`. The one thing CSS cannot express: a blur of what is *behind* a view. */\n 'expo-blur': ['ExpoBlurView', { defaultProps: { intensity: 50, tint: 'default' } }],\n /** `expo-video`. The player itself is a shared object the module hands out; this is its view. */\n 'expo-video': ['ExpoVideo', { viewName: 'VideoView' }],\n /**\n * `expo-camera`. Its module's first view, so its default: `ExpoCamera_CameraView` exists only on\n * iOS, after the Swift class, and commits as nothing on Android. `Camera` takes its pictures.\n */\n 'expo-camera': ['ExpoCamera'],\n /** `expo-apple-authentication`: Apple's Sign in with Apple button. iOS only. */\n 'apple-sign-in-button': ['ExpoAppleAuthentication'],\n /** `expo-symbols`: SF Symbols, iOS only, and the reason `icons` is not needed for them. */\n 'expo-symbol': ['SymbolModule', { defaultProps: { type: 'monochrome' } }],\n /** `expo-gl`, whose context is reached through an event rather than a prop. */\n 'expo-gl': ['ExpoGL'],\n /** `expo-glass-effect`: the iOS 26 material. Renders as a plain view where it is unavailable. */\n 'expo-glass': ['ExpoGlassEffect', { viewName: 'GlassView' }],\n /** Glass views that merge into one another when they come within `spacing` points. */\n 'expo-glass-container': ['ExpoGlassEffect', { viewName: 'GlassContainer' }],\n /** `expo-mesh-gradient`: the one gradient CSS cannot express, because it has no CSS spelling. */\n 'expo-mesh-gradient': ['ExpoMeshGradient', { viewName: 'MeshGradientView' }],\n /** `expo-live-photo`, iOS only. */\n 'expo-live-photo': ['ExpoLivePhoto', { viewName: 'LivePhotoView' }],\n /**\n * `expo-maps`, Google's on Android and Apple's on iOS: two modules, each with one view, so each\n * its module's default. `ExpoMaps` itself is the permissions module and has no view.\n * `MapView` (`<expo-map>`) types both as one element.\n */\n 'expo-maps-google': ['ExpoGoogleMaps'],\n 'expo-maps-apple': ['ExpoAppleMaps'],\n};\n\n/**\n * Register several of the known views by element name.\n *\n * ```ts\n * registerExpoViews('expo-image', 'expo-blur');\n * ```\n *\n * Named one at a time rather than all at once on purpose: registering a view for a module the app\n * has not installed produces an element that commits as nothing, and finding out at startup which\n * modules are present is not something JavaScript can do without importing them all.\n */\nexport function registerExpoViews(...elements: readonly (keyof typeof EXPO_VIEWS)[]): void {\n for (const element of elements) {\n const known = EXPO_VIEWS[element];\n if (!known) throw new Error(`[angular-native] no Expo view is known as '${element}'`);\n registerExpoView(element, known[0], known[1]);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"register-expo-view.js","sourceRoot":"","sources":["../src/register-expo-view.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Expo's native views, driven directly.\n *\n * An Expo module that renders something registers a Fabric component whose name it derives\n * rather than declares, and `requireNativeViewManager` is the only place that derivation is\n * written down. Everything else that function does is React: it caches a host component, builds\n * a view config for React's renderer, and wraps the result in a class whose whole job is to hold\n * a ref. None of that is reachable without React, and none of it is needed - the engine writes\n * props onto the shadow node itself, and Expo's Fabric views take their props as an untyped map,\n * so there is no codegen'd prop list to satisfy.\n *\n * What is left is the name. That is what this package is.\n *\n * No decorators and no Angular here on purpose: this is a registration, and the components it\n * registers are ordinary elements in a template.\n */\nimport { registerViewName, type PlatformViewName, type ViewNameOptions } from '@ng-native/fabric';\nimport { currentPlatform, optional } from './native.ts';\n\n/**\n * What Expo installs on the global when the app starts. Only the app identifier matters here;\n * `getViewConfig` is for React's renderer, which never runs.\n */\ninterface ExpoGlobal {\n readonly __expo_app_identifier__?: string;\n}\n\n/**\n * The Fabric component name for a module's view, exactly as `requireNativeViewManager` computes\n * it in `expo-modules-core`.\n *\n * The app identifier is the part that cannot be hardcoded. Expo Go runs many projects in one\n * binary, so it namespaces every view name with a per-app suffix; a standalone build has no\n * identifier and no suffix. Get it wrong and the view commits as `UnimplementedNativeView`\n * with no error, which is the same failure mode as a misspelled name.\n *\n * A module with more than one view names the extra ones (`viewName`); the default view is the\n * module itself.\n */\nexport function expoViewName(moduleName: string, viewName?: string): string {\n const identifier = (globalThis as { expo?: ExpoGlobal }).expo?.__expo_app_identifier__ ?? '';\n const suffix = identifier ? `_${identifier}` : '';\n const module = viewName ? `${moduleName}_${viewName}` : moduleName;\n return `ViewManagerAdapter_${module}${suffix}`;\n}\n\nexport interface ExpoViewOptions {\n /**\n * A named view on a module that has several. Omit for the module's default view. A module whose\n * view is named differently on each platform names both: `expo-video`'s is `VideoView` on iOS\n * and `SurfaceVideoView` on Android.\n */\n readonly viewName?: string | PlatformViewName;\n /**\n * Props the module's React component would have applied before native saw them. Expo's\n * wrappers do less of this than React Native's, but they do some: `expo-image` resolves a\n * source to an array and a `contentFit` to a string native understands.\n */\n readonly defaultProps?: Record<string, unknown>;\n /** Whether a component of the app's own with this selector is a plain view. */\n readonly yieldsToComponents?: boolean;\n /**\n * The prop the view reads its text from, where it takes no child views, as `@expo/ui`'s `Text`\n * reads `text`. The text written inside the element becomes that prop. See `ViewNameOptions`.\n */\n readonly textContent?: string;\n /** Rewrites the fonts the view's own props name. See `ViewNameOptions`. */\n readonly fontNames?: ViewNameOptions['fontNames'];\n}\n\n/**\n * Teach the engine an element name for an Expo module's view, e.g.\n * `registerExpoView('expo-image', 'ExpoImage')` makes `<expo-image>` commit as one.\n *\n * Call it at startup, before the first commit that uses the element. The app must have the\n * module installed so the native side registers the component; this is the JavaScript half only.\n */\nexport function registerExpoView(\n elementName: string,\n moduleName: string,\n options?: ExpoViewOptions,\n): void {\n const viewName = options?.viewName;\n registerViewName(\n elementName,\n typeof viewName === 'object'\n ? {\n ios: expoViewName(moduleName, viewName.ios),\n android: expoViewName(moduleName, viewName.android),\n }\n : expoViewName(moduleName, viewName),\n options?.defaultProps,\n {\n textContent: options?.textContent,\n yieldsToComponents: options?.yieldsToComponents,\n fontNames: options?.fontNames,\n },\n );\n optional(() =>\n (\n require('expo') as { requireNativeView(module: string, view?: string): unknown }\n ).requireNativeView(\n moduleName,\n typeof viewName === 'object'\n ? currentPlatform() === 'android'\n ? viewName.android\n : viewName.ios\n : viewName,\n ),\n );\n}\n\n/**\n * The views worth knowing the names of, so an app does not have to look each one up.\n *\n * Deliberately names and defaults, not components. An Angular component per Expo view, prop for\n * prop, is a maintenance burden with no end and a second place for every prop to be wrong; the\n * element plus the module's own documentation covers the common case, and an app that wants a\n * nicer surface writes the one component it actually needs.\n *\n * A module still has to be installed for its element to commit as anything: this is the\n * JavaScript half. An element whose native side is missing commits as `UnimplementedNativeView`.\n */\nexport const EXPO_VIEWS: Readonly<Record<string, [string, ExpoViewOptions?]>> = {\n /** `expo-image`. Takes `source` as a list, so a single `{uri}` is wrapped by the caller. */\n 'expo-image': ['ExpoImage', { defaultProps: { contentFit: 'cover' } }],\n /**\n * `expo-blur`. The one thing CSS cannot express: a blur of what is *behind* a view. The view is\n * `ExpoBlurView` on the `ExpoBlur` module, as `expo-blur`'s own `NativeBlurModule` asks for it.\n */\n 'expo-blur': [\n 'ExpoBlur',\n { viewName: 'ExpoBlurView', defaultProps: { intensity: 50, tint: 'default' } },\n ],\n /**\n * `expo-video`. The player itself is a shared object the module hands out; this is its view,\n * which has another name on Android, as `expo-video`'s own `NativeVideoView` asks for it. Bind\n * `player` to the player's `id` (`videoPlayer`'s), not to the player.\n */\n 'expo-video': ['ExpoVideo', { viewName: { ios: 'VideoView', android: 'SurfaceVideoView' } }],\n /**\n * `expo-camera`. Its module's first view, so its default: `ExpoCamera_CameraView` exists only on\n * iOS, after the Swift class, and commits as nothing on Android. `Camera` takes its pictures.\n */\n 'expo-camera': ['ExpoCamera'],\n /** `expo-apple-authentication`: Apple's Sign in with Apple button. iOS only. */\n 'apple-sign-in-button': ['ExpoAppleAuthentication'],\n /** `expo-symbols`: SF Symbols, iOS only, and the reason `icons` is not needed for them. */\n 'expo-symbol': ['SymbolModule', { defaultProps: { type: 'monochrome' } }],\n /** `expo-gl`, whose context is reached through an event rather than a prop. */\n 'expo-gl': ['ExpoGL'],\n /** `expo-glass-effect`: the iOS 26 material. Renders as a plain view where it is unavailable. */\n 'expo-glass': ['ExpoGlassEffect', { viewName: 'GlassView' }],\n /** Glass views that merge into one another when they come within `spacing` points. */\n 'expo-glass-container': ['ExpoGlassEffect', { viewName: 'GlassContainer' }],\n /** `expo-mesh-gradient`: the one gradient CSS cannot express, because it has no CSS spelling. */\n 'expo-mesh-gradient': ['ExpoMeshGradient', { viewName: 'MeshGradientView' }],\n /** `expo-live-photo`, iOS only. */\n 'expo-live-photo': ['ExpoLivePhoto', { viewName: 'LivePhotoView' }],\n /**\n * `expo-maps`, Google's on Android and Apple's on iOS: two modules, each with one view, so each\n * its module's default. `ExpoMaps` itself is the permissions module and has no view.\n * `MapView` (`<expo-map>`) types both as one element.\n */\n 'expo-maps-google': ['ExpoGoogleMaps'],\n 'expo-maps-apple': ['ExpoAppleMaps'],\n};\n\n/**\n * Register several of the known views by element name.\n *\n * ```ts\n * registerExpoViews('expo-image', 'expo-blur');\n * ```\n *\n * Named one at a time rather than all at once on purpose: registering a view for a module the app\n * has not installed produces an element that commits as nothing, and finding out at startup which\n * modules are present is not something JavaScript can do without importing them all.\n */\nexport function registerExpoViews(...elements: readonly (keyof typeof EXPO_VIEWS)[]): void {\n for (const element of elements) {\n const known = EXPO_VIEWS[element];\n if (!known) throw new Error(`[angular-native] no Expo view is known as '${element}'`);\n registerExpoView(element, known[0], known[1]);\n }\n}\n"]}
|
package/dist/search-field.d.ts
CHANGED
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
* `(cancel)`, `(searchFocus)` and `(searchBlur)`, and `focus()`, `blur()` and `clear()` from code.
|
|
17
17
|
* The text is a two-way `value` rather than the bar's `query`, because it is a Signal Forms control
|
|
18
18
|
* as `<text-input>` is: `[formField]` binds `value`, `disabled`, and `touch` on blur.
|
|
19
|
+
* Its type is the `font-family`, `font-size`, `font-weight` and `font-style` its CSS resolves to,
|
|
20
|
+
* as a `<text>`'s is.
|
|
19
21
|
*
|
|
20
22
|
* Expo Go cannot load native code of its own, so there the element draws nothing, and in
|
|
21
23
|
* development it says so once.
|
package/dist/search-field.js
CHANGED
|
@@ -16,12 +16,14 @@
|
|
|
16
16
|
* `(cancel)`, `(searchFocus)` and `(searchBlur)`, and `focus()`, `blur()` and `clear()` from code.
|
|
17
17
|
* The text is a two-way `value` rather than the bar's `query`, because it is a Signal Forms control
|
|
18
18
|
* as `<text-input>` is: `[formField]` binds `value`, `disabled`, and `touch` on blur.
|
|
19
|
+
* Its type is the `font-family`, `font-size`, `font-weight` and `font-style` its CSS resolves to,
|
|
20
|
+
* as a `<text>`'s is.
|
|
19
21
|
*
|
|
20
22
|
* Expo Go cannot load native code of its own, so there the element draws nothing, and in
|
|
21
23
|
* development it says so once.
|
|
22
24
|
*/
|
|
23
25
|
import { Component, booleanAttribute, DestroyRef, ElementRef, InjectionToken, inject, input, model, output, signal, } from '@angular/core';
|
|
24
|
-
import { Engine, HostEngine, registerViewName, } from '@ng-native/fabric';
|
|
26
|
+
import { Engine, HostEngine, nativePlatform, registerViewName, } from '@ng-native/fabric';
|
|
25
27
|
import { optional } from "./native.js";
|
|
26
28
|
import { expoViewName } from "./register-expo-view.js";
|
|
27
29
|
import { optionalBoolean } from "./transforms.js";
|
|
@@ -36,6 +38,15 @@ function warnMissing() {
|
|
|
36
38
|
console.warn('[angular-native] <search-field> is native code of @ng-native/expo, which this build does not ' +
|
|
37
39
|
'have, so it draws nothing. Expo Go cannot load it: run a development build.');
|
|
38
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* The field's face by the PostScript name UIKit finds it by: expo-font registers it under the name
|
|
43
|
+
* the app gives it, which `UIFont(name:)` does not know. Android's font manager finds that name.
|
|
44
|
+
*/
|
|
45
|
+
function fieldFontName(props, postScriptName) {
|
|
46
|
+
const family = props['fontFamily'];
|
|
47
|
+
if (typeof family === 'string')
|
|
48
|
+
props['fontFamily'] = postScriptName(family) ?? family;
|
|
49
|
+
}
|
|
39
50
|
export class SearchField {
|
|
40
51
|
/**
|
|
41
52
|
* The functions native defines on the view, or null where the build has no native search
|
|
@@ -87,7 +98,9 @@ export class SearchField {
|
|
|
87
98
|
node = inject(ElementRef).nativeElement;
|
|
88
99
|
engine = inject(Engine, { optional: true });
|
|
89
100
|
constructor() {
|
|
90
|
-
registerViewName('search-field', expoViewName(MODULE)
|
|
101
|
+
registerViewName('search-field', expoViewName(MODULE), undefined, {
|
|
102
|
+
fontNames: nativePlatform() === 'ios' ? fieldFontName : undefined,
|
|
103
|
+
});
|
|
91
104
|
if (this.missing)
|
|
92
105
|
warnMissing();
|
|
93
106
|
const host = this.node.host ?? inject(HostEngine);
|
package/dist/search-field.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search-field.js","sourceRoot":"","sources":["../src/search-field.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `<search-field>`: the platform's own search field, anywhere a view can go. `<native-search-bar>`\n * from `@ng-native/router` is the one in a navigation bar; this is the one for everywhere else, a\n * filter sheet, a page, a list's header:\n *\n * ```html\n * <ui-bottom-sheet [(open)]=\"filtering\">\n * <search-field placeholder=\"Search colours\" [(value)]=\"query\" />\n * ...\n * </ui-bottom-sheet>\n * ```\n *\n * It is `@ng-native/expo`'s own native view: UIKit's `UISearchBar` on iOS, with its Cancel button,\n * dictation and clear button, and AndroidX's `SearchView` on Android, which has no Cancel button.\n * The events are `<native-search-bar>`'s: `(search)` for the keyboard's search key with the text,\n * `(cancel)`, `(searchFocus)` and `(searchBlur)`, and `focus()`, `blur()` and `clear()` from code.\n * The text is a two-way `value` rather than the bar's `query`, because it is a Signal Forms control\n * as `<text-input>` is: `[formField]` binds `value`, `disabled`, and `touch` on blur.\n *\n * Expo Go cannot load native code of its own, so there the element draws nothing, and in\n * development it says so once.\n */\nimport {\n Component,\n booleanAttribute,\n DestroyRef,\n ElementRef,\n InjectionToken,\n inject,\n input,\n model,\n output,\n signal,\n} from '@angular/core';\nimport {\n Engine,\n type EngineNode,\n HostEngine,\n type NativeSyntheticEvent,\n registerViewName,\n} from '@ng-native/fabric';\nimport { optional } from './native.ts';\nimport { expoViewName } from './register-expo-view.ts';\nimport { optionalBoolean } from './transforms.ts';\n\n/** The functions the native module defines on the view, called with the view's tag as `this`. */\nexport interface SearchFieldFunctions {\n focus?(this: { nativeTag: number }): Promise<void>;\n blur?(this: { nativeTag: number }): Promise<void>;\n}\n\ndeclare const ngDevMode: unknown;\n\nconst MODULE = 'NgNativeSearchField';\n\nlet warned = false;\n\n/** Says once, in development, that the build has no native search field to draw. */\nfunction warnMissing(): void {\n if ((typeof ngDevMode !== 'undefined' && !ngDevMode) || warned) return;\n warned = true;\n console.warn(\n '[angular-native] <search-field> is native code of @ng-native/expo, which this build does not ' +\n 'have, so it draws nothing. Expo Go cannot load it: run a development build.',\n );\n}\n\n@Component({\n selector: 'search-field',\n template: '',\n styles: ':host { height: 56px; }',\n host: {\n '[text]': 'value()',\n '[enabled]': '!disabled()',\n '[mostRecentEventCount]': 'eventCount()',\n '[placeholder]': 'placeholder()',\n '[showsCancelButton]': 'showsCancelButton()',\n '[autoCapitalize]': 'autoCapitalize()',\n '[tintColor]': 'tintColor()',\n // Fabric draws a view the build does not have as a placeholder box naming it.\n '[style.display]': 'missing ? \"none\" : undefined',\n },\n})\nexport class SearchField {\n /**\n * The functions native defines on the view, or null where the build has no native search\n * field. Overridden in a test.\n */\n static readonly SOURCE = new InjectionToken<SearchFieldFunctions | null>(\n 'angular-native.searchFieldSource',\n {\n factory: () =>\n optional(() => {\n const core = require('expo-modules-core') as typeof import('expo-modules-core');\n const module = core.requireOptionalNativeModule<{\n ViewPrototypes?: Record<string, SearchFieldFunctions>;\n }>(MODULE);\n return module ? (module.ViewPrototypes?.[MODULE] ?? {}) : null;\n }),\n },\n );\n\n /**\n * The text in the field: typing writes it, and text set from code is put in the field. A\n * `model()`, which is all `FormValueControl<string>` asks, so `[formField]` binds it.\n */\n readonly value = model('');\n /** Refuse input and show the field dimmed. Signal Forms' `disabled()` rule sets it. */\n readonly disabled = input(false, { transform: booleanAttribute });\n readonly placeholder = input<string>();\n /**\n * iOS: whether the Cancel button shows. Left unset it shows while the field has the keyboard,\n * as in a search bar under a navigation bar.\n */\n readonly showsCancelButton = input(undefined, { transform: optionalBoolean });\n readonly autoCapitalize = input<'none' | 'words' | 'sentences' | 'characters'>();\n /** The colour of the cursor and the Cancel button. */\n readonly tintColor = input<string>();\n\n /** The keyboard's search key was pressed, with the text in the field. */\n readonly search = output<string>();\n /** iOS: the Cancel button was pressed. The field is emptied and gives up the keyboard. */\n readonly cancel = output<void>();\n readonly searchFocus = output<void>();\n readonly searchBlur = output<void>();\n /** The field gave up the keyboard: what Signal Forms marks a field touched from. */\n readonly touch = output<void>();\n\n /**\n * The keystroke the text answers, as React Native's text input counts them: native skips a text\n * older than what has been typed since, which a fast typist's echo otherwise overwrites.\n */\n protected readonly eventCount = signal(0);\n\n private readonly functions = inject(SearchField.SOURCE);\n protected readonly missing = !this.functions;\n private readonly node = inject(ElementRef).nativeElement as EngineNode;\n private readonly engine = inject(Engine, { optional: true });\n\n constructor() {\n registerViewName('search-field', expoViewName(MODULE));\n if (this.missing) warnMissing();\n const host =\n (this.node as { host?: Pick<Engine, 'setEventListener'> }).host ?? inject(HostEngine);\n const listen = (event: string, handle: (payload: Record<string, unknown>) => void) =>\n host.setEventListener(this.node, event, (raw) =>\n handle((raw as NativeSyntheticEvent<Record<string, unknown>>)?.nativeEvent ?? {}),\n );\n const stops = [\n listen('topChangeText', ({ text, eventCount }) => {\n if (typeof eventCount === 'number') this.eventCount.set(eventCount);\n this.value.set(String(text ?? ''));\n }),\n // Named apart from the outputs: a native event of an output's name reaches its listener too.\n listen('topSubmitSearch', ({ text }) => this.search.emit(String(text ?? this.value()))),\n listen('topPressCancel', () => this.cancel.emit()),\n listen('topBeginEditing', () => this.searchFocus.emit()),\n listen('topEndEditing', () => {\n this.searchBlur.emit();\n this.touch.emit();\n }),\n ];\n inject(DestroyRef).onDestroy(() => stops.forEach((stop) => stop()));\n }\n\n /** Give the field the keyboard. Does nothing before the view has reached the screen. */\n async focus(): Promise<void> {\n await this.call('focus');\n }\n\n /** Take the keyboard away from the field. */\n async blur(): Promise<void> {\n await this.call('blur');\n }\n\n /** Empty the field, and the value with it. */\n clear(): void {\n this.value.set('');\n }\n\n private async call(name: 'focus' | 'blur'): Promise<void> {\n const nativeTag = this.engine?.tagOf(this.node);\n if (nativeTag == null) return;\n await this.functions?.[name]?.call({ nativeTag });\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"search-field.js","sourceRoot":"","sources":["../src/search-field.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `<search-field>`: the platform's own search field, anywhere a view can go. `<native-search-bar>`\n * from `@ng-native/router` is the one in a navigation bar; this is the one for everywhere else, a\n * filter sheet, a page, a list's header:\n *\n * ```html\n * <ui-bottom-sheet [(open)]=\"filtering\">\n * <search-field placeholder=\"Search colours\" [(value)]=\"query\" />\n * ...\n * </ui-bottom-sheet>\n * ```\n *\n * It is `@ng-native/expo`'s own native view: UIKit's `UISearchBar` on iOS, with its Cancel button,\n * dictation and clear button, and AndroidX's `SearchView` on Android, which has no Cancel button.\n * The events are `<native-search-bar>`'s: `(search)` for the keyboard's search key with the text,\n * `(cancel)`, `(searchFocus)` and `(searchBlur)`, and `focus()`, `blur()` and `clear()` from code.\n * The text is a two-way `value` rather than the bar's `query`, because it is a Signal Forms control\n * as `<text-input>` is: `[formField]` binds `value`, `disabled`, and `touch` on blur.\n * Its type is the `font-family`, `font-size`, `font-weight` and `font-style` its CSS resolves to,\n * as a `<text>`'s is.\n *\n * Expo Go cannot load native code of its own, so there the element draws nothing, and in\n * development it says so once.\n */\nimport {\n Component,\n booleanAttribute,\n DestroyRef,\n ElementRef,\n InjectionToken,\n inject,\n input,\n model,\n output,\n signal,\n} from '@angular/core';\nimport {\n Engine,\n type EngineNode,\n HostEngine,\n type NativeSyntheticEvent,\n nativePlatform,\n type PostScriptNames,\n registerViewName,\n} from '@ng-native/fabric';\nimport { optional } from './native.ts';\nimport { expoViewName } from './register-expo-view.ts';\nimport { optionalBoolean } from './transforms.ts';\n\n/** The functions the native module defines on the view, called with the view's tag as `this`. */\nexport interface SearchFieldFunctions {\n focus?(this: { nativeTag: number }): Promise<void>;\n blur?(this: { nativeTag: number }): Promise<void>;\n}\n\ndeclare const ngDevMode: unknown;\n\nconst MODULE = 'NgNativeSearchField';\n\nlet warned = false;\n\n/** Says once, in development, that the build has no native search field to draw. */\nfunction warnMissing(): void {\n if ((typeof ngDevMode !== 'undefined' && !ngDevMode) || warned) return;\n warned = true;\n console.warn(\n '[angular-native] <search-field> is native code of @ng-native/expo, which this build does not ' +\n 'have, so it draws nothing. Expo Go cannot load it: run a development build.',\n );\n}\n\n/**\n * The field's face by the PostScript name UIKit finds it by: expo-font registers it under the name\n * the app gives it, which `UIFont(name:)` does not know. Android's font manager finds that name.\n */\nfunction fieldFontName(props: Record<string, unknown>, postScriptName: PostScriptNames): void {\n const family = props['fontFamily'];\n if (typeof family === 'string') props['fontFamily'] = postScriptName(family) ?? family;\n}\n\n@Component({\n selector: 'search-field',\n template: '',\n styles: ':host { height: 56px; }',\n host: {\n '[text]': 'value()',\n '[enabled]': '!disabled()',\n '[mostRecentEventCount]': 'eventCount()',\n '[placeholder]': 'placeholder()',\n '[showsCancelButton]': 'showsCancelButton()',\n '[autoCapitalize]': 'autoCapitalize()',\n '[tintColor]': 'tintColor()',\n // Fabric draws a view the build does not have as a placeholder box naming it.\n '[style.display]': 'missing ? \"none\" : undefined',\n },\n})\nexport class SearchField {\n /**\n * The functions native defines on the view, or null where the build has no native search\n * field. Overridden in a test.\n */\n static readonly SOURCE = new InjectionToken<SearchFieldFunctions | null>(\n 'angular-native.searchFieldSource',\n {\n factory: () =>\n optional(() => {\n const core = require('expo-modules-core') as typeof import('expo-modules-core');\n const module = core.requireOptionalNativeModule<{\n ViewPrototypes?: Record<string, SearchFieldFunctions>;\n }>(MODULE);\n return module ? (module.ViewPrototypes?.[MODULE] ?? {}) : null;\n }),\n },\n );\n\n /**\n * The text in the field: typing writes it, and text set from code is put in the field. A\n * `model()`, which is all `FormValueControl<string>` asks, so `[formField]` binds it.\n */\n readonly value = model('');\n /** Refuse input and show the field dimmed. Signal Forms' `disabled()` rule sets it. */\n readonly disabled = input(false, { transform: booleanAttribute });\n readonly placeholder = input<string>();\n /**\n * iOS: whether the Cancel button shows. Left unset it shows while the field has the keyboard,\n * as in a search bar under a navigation bar.\n */\n readonly showsCancelButton = input(undefined, { transform: optionalBoolean });\n readonly autoCapitalize = input<'none' | 'words' | 'sentences' | 'characters'>();\n /** The colour of the cursor and the Cancel button. */\n readonly tintColor = input<string>();\n\n /** The keyboard's search key was pressed, with the text in the field. */\n readonly search = output<string>();\n /** iOS: the Cancel button was pressed. The field is emptied and gives up the keyboard. */\n readonly cancel = output<void>();\n readonly searchFocus = output<void>();\n readonly searchBlur = output<void>();\n /** The field gave up the keyboard: what Signal Forms marks a field touched from. */\n readonly touch = output<void>();\n\n /**\n * The keystroke the text answers, as React Native's text input counts them: native skips a text\n * older than what has been typed since, which a fast typist's echo otherwise overwrites.\n */\n protected readonly eventCount = signal(0);\n\n private readonly functions = inject(SearchField.SOURCE);\n protected readonly missing = !this.functions;\n private readonly node = inject(ElementRef).nativeElement as EngineNode;\n private readonly engine = inject(Engine, { optional: true });\n\n constructor() {\n registerViewName('search-field', expoViewName(MODULE), undefined, {\n fontNames: nativePlatform() === 'ios' ? fieldFontName : undefined,\n });\n if (this.missing) warnMissing();\n const host =\n (this.node as { host?: Pick<Engine, 'setEventListener'> }).host ?? inject(HostEngine);\n const listen = (event: string, handle: (payload: Record<string, unknown>) => void) =>\n host.setEventListener(this.node, event, (raw) =>\n handle((raw as NativeSyntheticEvent<Record<string, unknown>>)?.nativeEvent ?? {}),\n );\n const stops = [\n listen('topChangeText', ({ text, eventCount }) => {\n if (typeof eventCount === 'number') this.eventCount.set(eventCount);\n this.value.set(String(text ?? ''));\n }),\n // Named apart from the outputs: a native event of an output's name reaches its listener too.\n listen('topSubmitSearch', ({ text }) => this.search.emit(String(text ?? this.value()))),\n listen('topPressCancel', () => this.cancel.emit()),\n listen('topBeginEditing', () => this.searchFocus.emit()),\n listen('topEndEditing', () => {\n this.searchBlur.emit();\n this.touch.emit();\n }),\n ];\n inject(DestroyRef).onDestroy(() => stops.forEach((stop) => stop()));\n }\n\n /** Give the field the keyboard. Does nothing before the view has reached the screen. */\n async focus(): Promise<void> {\n await this.call('focus');\n }\n\n /** Take the keyboard away from the field. */\n async blur(): Promise<void> {\n await this.call('blur');\n }\n\n /** Empty the field, and the value with it. */\n clear(): void {\n this.value.set('');\n }\n\n private async call(name: 'focus' | 'blur'): Promise<void> {\n const nativeTag = this.engine?.tagOf(this.node);\n if (nativeTag == null) return;\n await this.functions?.[name]?.call({ nativeTag });\n }\n}\n"]}
|
|
@@ -11,6 +11,8 @@ export declare class UiBottomSheetView {
|
|
|
11
11
|
readonly fitToContents: import("@angular/core").InputSignal<boolean | undefined>;
|
|
12
12
|
/** Compose: whether the drag handle is drawn. */
|
|
13
13
|
readonly showDragHandle: import("@angular/core").InputSignal<boolean | undefined>;
|
|
14
|
+
/** Compose: the sheet's colour behind its content. A colour prop, so the engine converts it. */
|
|
15
|
+
readonly containerColor: import("@angular/core").InputSignal<unknown>;
|
|
14
16
|
/** Compose: the sheet opens at its full height rather than resting at half the screen's. */
|
|
15
17
|
readonly skipPartiallyExpanded: import("@angular/core").InputSignal<boolean | undefined>;
|
|
16
18
|
/** SwiftUI: the user dismissed the sheet, or it was presented by something other than the prop. */
|
|
@@ -21,5 +23,5 @@ export declare class UiBottomSheetView {
|
|
|
21
23
|
readonly dismissRequest: import("@angular/core").OutputEmitterRef<NativeSyntheticEvent<Record<string, never>>>;
|
|
22
24
|
constructor();
|
|
23
25
|
static ɵfac: i0.ɵɵFactoryDeclaration<UiBottomSheetView, never>;
|
|
24
|
-
static ɵcmp: i0.ɵɵComponentDeclaration<UiBottomSheetView, "ui-bottom-sheet-view", never, { "isPresented": { "alias": "isPresented"; "required": false; "isSignal": true; }; "fitToContents": { "alias": "fitToContents"; "required": false; "isSignal": true; }; "showDragHandle": { "alias": "showDragHandle"; "required": false; "isSignal": true; }; "skipPartiallyExpanded": { "alias": "skipPartiallyExpanded"; "required": false; "isSignal": true; }; }, { "isPresentedChange": "isPresentedChange"; "dismiss": "dismiss"; "dismissRequest": "dismissRequest"; }, never, ["*"], true, never>;
|
|
26
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<UiBottomSheetView, "ui-bottom-sheet-view", never, { "isPresented": { "alias": "isPresented"; "required": false; "isSignal": true; }; "fitToContents": { "alias": "fitToContents"; "required": false; "isSignal": true; }; "showDragHandle": { "alias": "showDragHandle"; "required": false; "isSignal": true; }; "containerColor": { "alias": "containerColor"; "required": false; "isSignal": true; }; "skipPartiallyExpanded": { "alias": "skipPartiallyExpanded"; "required": false; "isSignal": true; }; }, { "isPresentedChange": "isPresentedChange"; "dismiss": "dismiss"; "dismissRequest": "dismissRequest"; }, never, ["*"], true, never>;
|
|
25
27
|
}
|
|
@@ -23,6 +23,9 @@ export class UiBottomSheetView {
|
|
|
23
23
|
/** Compose: whether the drag handle is drawn. */
|
|
24
24
|
showDragHandle = input(/* @ts-ignore */
|
|
25
25
|
...(ngDevMode ? [undefined, { debugName: "showDragHandle" }] : /* istanbul ignore next */ []));
|
|
26
|
+
/** Compose: the sheet's colour behind its content. A colour prop, so the engine converts it. */
|
|
27
|
+
containerColor = input(/* @ts-ignore */
|
|
28
|
+
...(ngDevMode ? [undefined, { debugName: "containerColor" }] : /* istanbul ignore next */ []));
|
|
26
29
|
/** Compose: the sheet opens at its full height rather than resting at half the screen's. */
|
|
27
30
|
skipPartiallyExpanded = input(/* @ts-ignore */
|
|
28
31
|
...(ngDevMode ? [undefined, { debugName: "skipPartiallyExpanded" }] : /* istanbul ignore next */ []));
|
|
@@ -37,7 +40,7 @@ export class UiBottomSheetView {
|
|
|
37
40
|
keepNativeView(inject(ElementRef).nativeElement);
|
|
38
41
|
}
|
|
39
42
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: UiBottomSheetView, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
40
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.2.0", type: UiBottomSheetView, isStandalone: true, selector: "ui-bottom-sheet-view", inputs: { isPresented: { classPropertyName: "isPresented", publicName: "isPresented", isSignal: true, isRequired: false, transformFunction: null }, fitToContents: { classPropertyName: "fitToContents", publicName: "fitToContents", isSignal: true, isRequired: false, transformFunction: null }, showDragHandle: { classPropertyName: "showDragHandle", publicName: "showDragHandle", isSignal: true, isRequired: false, transformFunction: null }, skipPartiallyExpanded: { classPropertyName: "skipPartiallyExpanded", publicName: "skipPartiallyExpanded", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { isPresentedChange: "isPresentedChange", dismiss: "dismiss", dismissRequest: "dismissRequest" }, host: { properties: { "isPresented": "isPresented()", "fitToContents": "fitToContents()", "showDragHandle": "showDragHandle()", "skipPartiallyExpanded": "skipPartiallyExpanded()" } }, ngImport: i0, template: '<ng-content />', isInline: true });
|
|
43
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.2.0", type: UiBottomSheetView, isStandalone: true, selector: "ui-bottom-sheet-view", inputs: { isPresented: { classPropertyName: "isPresented", publicName: "isPresented", isSignal: true, isRequired: false, transformFunction: null }, fitToContents: { classPropertyName: "fitToContents", publicName: "fitToContents", isSignal: true, isRequired: false, transformFunction: null }, showDragHandle: { classPropertyName: "showDragHandle", publicName: "showDragHandle", isSignal: true, isRequired: false, transformFunction: null }, containerColor: { classPropertyName: "containerColor", publicName: "containerColor", isSignal: true, isRequired: false, transformFunction: null }, skipPartiallyExpanded: { classPropertyName: "skipPartiallyExpanded", publicName: "skipPartiallyExpanded", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { isPresentedChange: "isPresentedChange", dismiss: "dismiss", dismissRequest: "dismissRequest" }, host: { properties: { "isPresented": "isPresented()", "fitToContents": "fitToContents()", "showDragHandle": "showDragHandle()", "containerColor": "containerColor()", "skipPartiallyExpanded": "skipPartiallyExpanded()" } }, ngImport: i0, template: '<ng-content />', isInline: true });
|
|
41
44
|
}
|
|
42
45
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: UiBottomSheetView, decorators: [{
|
|
43
46
|
type: Component,
|
|
@@ -48,8 +51,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImpor
|
|
|
48
51
|
'[isPresented]': 'isPresented()',
|
|
49
52
|
'[fitToContents]': 'fitToContents()',
|
|
50
53
|
'[showDragHandle]': 'showDragHandle()',
|
|
54
|
+
'[containerColor]': 'containerColor()',
|
|
51
55
|
'[skipPartiallyExpanded]': 'skipPartiallyExpanded()',
|
|
52
56
|
},
|
|
53
57
|
}]
|
|
54
|
-
}], ctorParameters: () => [], propDecorators: { isPresented: [{ type: i0.Input, args: [{ isSignal: true, alias: "isPresented", required: false }] }], fitToContents: [{ type: i0.Input, args: [{ isSignal: true, alias: "fitToContents", required: false }] }], showDragHandle: [{ type: i0.Input, args: [{ isSignal: true, alias: "showDragHandle", required: false }] }], skipPartiallyExpanded: [{ type: i0.Input, args: [{ isSignal: true, alias: "skipPartiallyExpanded", required: false }] }], isPresentedChange: [{ type: i0.Output, args: ["isPresentedChange"] }], dismiss: [{ type: i0.Output, args: ["dismiss"] }], dismissRequest: [{ type: i0.Output, args: ["dismissRequest"] }] } });
|
|
58
|
+
}], ctorParameters: () => [], propDecorators: { isPresented: [{ type: i0.Input, args: [{ isSignal: true, alias: "isPresented", required: false }] }], fitToContents: [{ type: i0.Input, args: [{ isSignal: true, alias: "fitToContents", required: false }] }], showDragHandle: [{ type: i0.Input, args: [{ isSignal: true, alias: "showDragHandle", required: false }] }], containerColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "containerColor", required: false }] }], skipPartiallyExpanded: [{ type: i0.Input, args: [{ isSignal: true, alias: "skipPartiallyExpanded", required: false }] }], isPresentedChange: [{ type: i0.Output, args: ["isPresentedChange"] }], dismiss: [{ type: i0.Output, args: ["dismiss"] }], dismissRequest: [{ type: i0.Output, args: ["dismissRequest"] }] } });
|
|
55
59
|
//# sourceMappingURL=ui-bottom-sheet-view.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ui-bottom-sheet-view.js","sourceRoot":"","sources":["../src/ui-bottom-sheet-view.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAC7E,OAAO,EAA6B,cAAc,EAAE,MAAM,mBAAmB,CAAC;;
|
|
1
|
+
{"version":3,"file":"ui-bottom-sheet-view.js","sourceRoot":"","sources":["../src/ui-bottom-sheet-view.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAC7E,OAAO,EAA6B,cAAc,EAAE,MAAM,mBAAmB,CAAC;;AAgB9E,MAAM,OAAO,iBAAiB;IAC5B,sFAAsF;IAC7E,WAAW,GAAG,KAAK;+FAAW,CAAC;IACxC,oEAAoE;IAC3D,aAAa,GAAG,KAAK;iGAAW,CAAC;IAC1C,iDAAiD;IACxC,cAAc,GAAG,KAAK;kGAAW,CAAC;IAC3C,gGAAgG;IACvF,cAAc,GAAG,KAAK;kGAAW,CAAC;IAC3C,4FAA4F;IACnF,qBAAqB,GAAG,KAAK;yGAAW,CAAC;IAClD,mGAAmG;IAC1F,iBAAiB,GAAG,MAAM,EAA+B,CAAC;IACnE,wEAAwE;IAC/D,OAAO,GAAG,MAAM,EAA+C,CAAC;IACzE,mFAAmF;IAC1E,cAAc,GAAG,MAAM,EAA+C,CAAC;IAEhF;QACE,iFAAiF;QACjF,cAAc,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,aAAa,CAAC,CAAC;IACnD,CAAC;uGArBU,iBAAiB;2FAAjB,iBAAiB,6oCATlB,gBAAgB;;2FASf,iBAAiB;kBAX7B,SAAS;mBAAC;oBACT,QAAQ,EAAE,sBAAsB;oBAChC,QAAQ,EAAE,gBAAgB;oBAC1B,IAAI,EAAE;wBACJ,eAAe,EAAE,eAAe;wBAChC,iBAAiB,EAAE,iBAAiB;wBACpC,kBAAkB,EAAE,kBAAkB;wBACtC,kBAAkB,EAAE,kBAAkB;wBACtC,yBAAyB,EAAE,yBAAyB;qBACrD;iBACF","sourcesContent":["/**\n * `<ui-bottom-sheet-view>`: `@expo/ui`'s sheet itself, with each platform's props typed.\n *\n * `UiBottomSheet` in `expo-ui-components.ts` is the component an app uses, and this is the native\n * view inside it: SwiftUI's `BottomSheetView` on iOS, Compose's `ModalBottomSheetView` on Android.\n * It has an element name of its own because `ui-bottom-sheet` is that component's, and a\n * component's template takes its own selector to be the component again.\n *\n * The two views share no prop. An input left unset never reaches native, so the component sets\n * the ones the platform reads. The events are outputs for their type only, as with the other\n * `@expo/ui` components: the element's own event is what arrives.\n */\nimport { Component, ElementRef, inject, input, output } from '@angular/core';\nimport { type NativeSyntheticEvent, keepNativeView } from '@ng-native/fabric';\n\n/** What SwiftUI's sheet sends when the user dismisses it: that it is no longer presented. */\nexport type UiBottomSheetPresentedEvent = NativeSyntheticEvent<{ readonly isPresented: boolean }>;\n\n@Component({\n selector: 'ui-bottom-sheet-view',\n template: '<ng-content />',\n host: {\n '[isPresented]': 'isPresented()',\n '[fitToContents]': 'fitToContents()',\n '[showDragHandle]': 'showDragHandle()',\n '[containerColor]': 'containerColor()',\n '[skipPartiallyExpanded]': 'skipPartiallyExpanded()',\n },\n})\nexport class UiBottomSheetView {\n /** SwiftUI: whether the sheet is presented. The view stays in the tree either way. */\n readonly isPresented = input<boolean>();\n /** SwiftUI: the sheet's one detent is the height of its content. */\n readonly fitToContents = input<boolean>();\n /** Compose: whether the drag handle is drawn. */\n readonly showDragHandle = input<boolean>();\n /** Compose: the sheet's colour behind its content. A colour prop, so the engine converts it. */\n readonly containerColor = input<unknown>();\n /** Compose: the sheet opens at its full height rather than resting at half the screen's. */\n readonly skipPartiallyExpanded = input<boolean>();\n /** SwiftUI: the user dismissed the sheet, or it was presented by something other than the prop. */\n readonly isPresentedChange = output<UiBottomSheetPresentedEvent>();\n /** SwiftUI: the sheet has finished dismissing, whoever dismissed it. */\n readonly dismiss = output<NativeSyntheticEvent<Record<string, never>>>();\n /** Compose: the user dismissed the sheet, which stays until it leaves the tree. */\n readonly dismissRequest = output<NativeSyntheticEvent<Record<string, never>>>();\n\n constructor() {\n // The host is the registered native view, not a plain one: see `keepNativeView`.\n keepNativeView(inject(ElementRef).nativeElement);\n }\n}\n"]}
|
package/dist/video.js
CHANGED
package/dist/video.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"video.js","sourceRoot":"","sources":["../src/video.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EAAE,SAAS,EAAE,WAAW,EAAe,MAAM,aAAa,CAAC;AAElE,oFAAoF;AACpF,MAAM,UAAU,WAAW,CACzB,MAAwC,EACxC,UAAmC,EAAE;IAErC,MAAM,IAAI,GAAG,aAAa,CACxB,YAAY,EACZ,GAAG,EAAE,CAAC,OAAO,CAAC,YAAY,CAAgC,EAC1D,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,CAAC,CAC1B,CAAC;IACF,IAAI,CAAC,IAAI;QAAE,MAAM,WAAW,CAAC,YAAY,EAAE,sCAAsC,CAAC,CAAC;IACnF,MAAM,MAAM,GAAG,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;IAC9C,OAAO,SAAS,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAC/D,CAAC","sourcesContent":["/**\n * `videoPlayer`, an `expo-video` player owned by the component that makes it.\n *\n * ```ts\n * protected readonly player = videoPlayer(this.source, { timeUpdate: 0.5 });\n *\n * // <expo-video [player]=\"player.
|
|
1
|
+
{"version":3,"file":"video.js","sourceRoot":"","sources":["../src/video.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EAAE,SAAS,EAAE,WAAW,EAAe,MAAM,aAAa,CAAC;AAElE,oFAAoF;AACpF,MAAM,UAAU,WAAW,CACzB,MAAwC,EACxC,UAAmC,EAAE;IAErC,MAAM,IAAI,GAAG,aAAa,CACxB,YAAY,EACZ,GAAG,EAAE,CAAC,OAAO,CAAC,YAAY,CAAgC,EAC1D,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,CAAC,CAC1B,CAAC;IACF,IAAI,CAAC,IAAI;QAAE,MAAM,WAAW,CAAC,YAAY,EAAE,sCAAsC,CAAC,CAAC;IACnF,MAAM,MAAM,GAAG,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;IAC9C,OAAO,SAAS,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAC/D,CAAC","sourcesContent":["/**\n * `videoPlayer`, an `expo-video` player owned by the component that makes it.\n *\n * ```ts\n * protected readonly player = videoPlayer(this.source, { timeUpdate: 0.5 });\n *\n * // <expo-video [player]=\"player.id\" />\n * // {{ player.state().currentTime }}\n * ```\n */\nimport { expoModuleNow, unavailable } from './native.ts';\nimport { ownPlayer, watchPlayer, type Player } from './player.ts';\n\n/** A video player owned by the current component, released when it is destroyed. */\nexport function videoPlayer(\n source: import('expo-video').VideoSource,\n options: { timeUpdate?: number } = {},\n): Player<import('expo-video').VideoPlayer> {\n const expo = expoModuleNow(\n 'expo-video',\n () => require('expo-video') as typeof import('expo-video'),\n ['ios', 'android', 'web'],\n );\n if (!expo) throw unavailable('expo-video', 'Stand in for the player in the test.');\n const native = expo.createVideoPlayer(source);\n return ownPlayer(native, () => watchPlayer(native, options));\n}\n"]}
|
package/ios/SearchField.swift
CHANGED
|
@@ -45,6 +45,20 @@ public final class SearchFieldModule: Module {
|
|
|
45
45
|
Prop("tintColor") { (view: SearchFieldView, color: UIColor?) in
|
|
46
46
|
view.searchBar.tintColor = color
|
|
47
47
|
}
|
|
48
|
+
// The face and size the field's CSS resolves to, as a text's: the face by its PostScript name.
|
|
49
|
+
Prop("fontFamily") { (view: SearchFieldView, family: String?) in
|
|
50
|
+
view.fontFamily = family
|
|
51
|
+
}
|
|
52
|
+
Prop("fontSize") { (view: SearchFieldView, size: Double?) in
|
|
53
|
+
view.fontSize = size.map { CGFloat($0) }
|
|
54
|
+
}
|
|
55
|
+
// A weight and a slant no matched face already draws: a number, or `normal` and `bold`.
|
|
56
|
+
Prop("fontWeight") { (view: SearchFieldView, weight: Either<Double, String>?) in
|
|
57
|
+
view.fontWeight = weight.flatMap(SearchFieldView.weight)
|
|
58
|
+
}
|
|
59
|
+
Prop("fontStyle") { (view: SearchFieldView, style: String?) in
|
|
60
|
+
view.italic = style == "italic"
|
|
61
|
+
}
|
|
48
62
|
|
|
49
63
|
AsyncFunction("focus") { (view: SearchFieldView) in
|
|
50
64
|
view.searchBar.becomeFirstResponder()
|
|
@@ -72,6 +86,20 @@ final class SearchFieldView: ExpoView, UISearchBarDelegate {
|
|
|
72
86
|
var text = "" {
|
|
73
87
|
didSet { applyText() }
|
|
74
88
|
}
|
|
89
|
+
/// The field's own font, put back when the CSS names none.
|
|
90
|
+
private lazy var systemFont = searchBar.searchTextField.font ?? .preferredFont(forTextStyle: .body)
|
|
91
|
+
var fontFamily: String? {
|
|
92
|
+
didSet { applyFont() }
|
|
93
|
+
}
|
|
94
|
+
var fontSize: CGFloat? {
|
|
95
|
+
didSet { applyFont() }
|
|
96
|
+
}
|
|
97
|
+
var fontWeight: UIFont.Weight? {
|
|
98
|
+
didSet { applyFont() }
|
|
99
|
+
}
|
|
100
|
+
var italic = false {
|
|
101
|
+
didSet { applyFont() }
|
|
102
|
+
}
|
|
75
103
|
/// Nil shows the Cancel button while the field has the keyboard.
|
|
76
104
|
var showsCancelButton: Bool? {
|
|
77
105
|
didSet { searchBar.setShowsCancelButton(showsCancelButton ?? searchBar.isFirstResponder, animated: false) }
|
|
@@ -89,6 +117,48 @@ final class SearchFieldView: ExpoView, UISearchBarDelegate {
|
|
|
89
117
|
searchBar.frame = bounds
|
|
90
118
|
}
|
|
91
119
|
|
|
120
|
+
/// The CSS's face at its size, weight and slant, scaled with the text size setting as a text's is,
|
|
121
|
+
/// or the field's own font when the CSS names none of them. A name no face is registered under is
|
|
122
|
+
/// looked up as a family; one the system does not have either keeps the system face.
|
|
123
|
+
private func applyFont() {
|
|
124
|
+
let field = searchBar.searchTextField
|
|
125
|
+
guard fontFamily != nil || fontSize != nil || fontWeight != nil || italic else {
|
|
126
|
+
field.adjustsFontForContentSizeCategory = false
|
|
127
|
+
field.font = systemFont
|
|
128
|
+
return
|
|
129
|
+
}
|
|
130
|
+
let size = fontSize ?? systemFont.pointSize
|
|
131
|
+
var font = fontWeight.map { UIFont.systemFont(ofSize: size, weight: $0) } ?? systemFont.withSize(size)
|
|
132
|
+
if let family = fontFamily {
|
|
133
|
+
let named = UIFont(name: family, size: size)
|
|
134
|
+
let familyName = named?.familyName ?? (UIFont.fontNames(forFamilyName: family).isEmpty ? nil : family)
|
|
135
|
+
if let familyName, let weight = fontWeight {
|
|
136
|
+
font = UIFont(descriptor: UIFontDescriptor(fontAttributes: [
|
|
137
|
+
.family: familyName,
|
|
138
|
+
.traits: [UIFontDescriptor.TraitKey.weight: weight],
|
|
139
|
+
]), size: size)
|
|
140
|
+
} else if let named {
|
|
141
|
+
font = named
|
|
142
|
+
} else if let familyName {
|
|
143
|
+
font = UIFont(descriptor: UIFontDescriptor(fontAttributes: [.family: familyName]), size: size)
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if italic, let slanted = font.fontDescriptor.withSymbolicTraits(font.fontDescriptor.symbolicTraits.union(.traitItalic)) {
|
|
147
|
+
font = UIFont(descriptor: slanted, size: size)
|
|
148
|
+
}
|
|
149
|
+
field.font = UIFontMetrics.default.scaledFont(for: font)
|
|
150
|
+
field.adjustsFontForContentSizeCategory = true
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/// A CSS weight as UIKit's nearest, as React Native maps a text's.
|
|
154
|
+
static func weight(_ value: Either<Double, String>) -> UIFont.Weight? {
|
|
155
|
+
let string: String? = value.get()
|
|
156
|
+
let css: Double? = value.get() ?? (string == "bold" ? 700 : string == "normal" ? 400 : string.flatMap(Double.init))
|
|
157
|
+
guard let css else { return nil }
|
|
158
|
+
let weights: [UIFont.Weight] = [.ultraLight, .thin, .light, .regular, .medium, .semibold, .bold, .heavy, .black]
|
|
159
|
+
return weights[min(max(Int((css / 100).rounded()) - 1, 0), weights.count - 1)]
|
|
160
|
+
}
|
|
161
|
+
|
|
92
162
|
/// Puts JavaScript's text in the field, unless it answers a keystroke older than the last one.
|
|
93
163
|
private func applyText() {
|
|
94
164
|
guard mostRecentEventCount >= eventCount, searchBar.text != text else { return }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ng-native/expo",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0",
|
|
4
4
|
"description": "Expo modules as Angular services for Angular Native, plus registerExpoView for a module's native view.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"angular",
|
|
@@ -217,7 +217,7 @@
|
|
|
217
217
|
"access": "public"
|
|
218
218
|
},
|
|
219
219
|
"dependencies": {
|
|
220
|
-
"@ng-native/fabric": "0.
|
|
220
|
+
"@ng-native/fabric": "0.16.0"
|
|
221
221
|
},
|
|
222
222
|
"nx": {
|
|
223
223
|
"tags": [
|