@ng-native/expo 0.13.0 → 0.15.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 +4 -0
- package/android/src/main/java/dev/ngnative/expo/SearchFieldModule.kt +172 -0
- package/dist/app-info.d.ts +18 -15
- package/dist/app-info.js +17 -5
- package/dist/app-info.js.map +1 -1
- package/dist/apple-sign-in.js +5 -4
- package/dist/apple-sign-in.js.map +1 -1
- package/dist/assets.js +2 -2
- package/dist/assets.js.map +1 -1
- package/dist/async-storage.js +4 -1
- package/dist/async-storage.js.map +1 -1
- package/dist/audio.js +2 -2
- package/dist/audio.js.map +1 -1
- package/dist/battery.js +4 -1
- package/dist/battery.js.map +1 -1
- package/dist/biometrics.js +5 -4
- package/dist/biometrics.js.map +1 -1
- package/dist/brightness.js +7 -2
- package/dist/brightness.js.map +1 -1
- package/dist/camera.d.ts +28 -2
- package/dist/camera.js +30 -3
- package/dist/camera.js.map +1 -1
- package/dist/clipboard.js +5 -2
- package/dist/clipboard.js.map +1 -1
- package/dist/database.js +2 -2
- package/dist/database.js.map +1 -1
- package/dist/expo-ui-components.d.ts +127 -12
- package/dist/expo-ui-components.js +354 -48
- package/dist/expo-ui-components.js.map +1 -1
- package/dist/expo-ui.js +7 -1
- package/dist/expo-ui.js.map +1 -1
- package/dist/foldable.d.ts +2 -0
- package/dist/foldable.js +15 -5
- package/dist/foldable.js.map +1 -1
- package/dist/fonts.d.ts +2 -0
- package/dist/fonts.js +12 -14
- package/dist/fonts.js.map +1 -1
- package/dist/haptics.js +2 -2
- package/dist/haptics.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/language-model.js +7 -3
- package/dist/language-model.js.map +1 -1
- package/dist/native.d.ts +17 -2
- package/dist/native.js +61 -3
- package/dist/native.js.map +1 -1
- package/dist/network.js +4 -1
- package/dist/network.js.map +1 -1
- package/dist/notifications.js +35 -8
- package/dist/notifications.js.map +1 -1
- package/dist/observed.js +5 -1
- package/dist/observed.js.map +1 -1
- package/dist/orientation.js +4 -1
- package/dist/orientation.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 +25 -2
- package/dist/register-expo-view.js +28 -7
- package/dist/register-expo-view.js.map +1 -1
- package/dist/screen-capture.js +2 -2
- package/dist/screen-capture.js.map +1 -1
- package/dist/search-field.d.ts +85 -0
- package/dist/search-field.js +160 -0
- package/dist/search-field.js.map +1 -0
- package/dist/secure-store.js +4 -1
- package/dist/secure-store.js.map +1 -1
- package/dist/sensors.js +6 -3
- package/dist/sensors.js.map +1 -1
- package/dist/splash-screen.js +2 -2
- package/dist/splash-screen.js.map +1 -1
- package/dist/store.js +4 -0
- package/dist/store.js.map +1 -1
- package/dist/tracking.js +2 -2
- package/dist/tracking.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/ui-font-names.d.ts +16 -0
- package/dist/ui-font-names.js +71 -0
- package/dist/ui-font-names.js.map +1 -0
- package/dist/updates.js +4 -1
- package/dist/updates.js.map +1 -1
- package/dist/video.js +3 -3
- package/dist/video.js.map +1 -1
- package/dist/watch.js +6 -5
- package/dist/watch.js.map +1 -1
- package/expo-module.config.json +5 -2
- package/ios/ExpoUIModifiersModule.swift +25 -0
- package/ios/InlineNavigationTitle.swift +12 -0
- package/ios/NgNativeExpo.podspec +3 -0
- package/ios/SearchField.swift +200 -0
- package/ios/SegmentedFont.swift +113 -0
- package/package.json +6 -2
package/dist/watch.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"watch.js","sourceRoot":"","sources":["../src/watch.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,YAAY,EACZ,cAAc,EACd,OAAO,EACP,MAAM,EACN,MAAM,GAEP,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;;AA4EzC,MAAM,MAAM,GAAG;IACb,CAAC,kBAAkB,EAAE,YAAY,CAAC;IAClC,CAAC,2BAA2B,EAAE,qBAAqB,CAAC;IACpD,CAAC,oCAAoC,EAAE,8BAA8B,CAAC;IACtE,CAAC,iBAAiB,EAAE,WAAW,CAAC;IAChC,CAAC,qBAAqB,EAAE,eAAe,CAAC;CAChC,CAAC;AAEX,MAAM,aAAa,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;AAGlD,MAAM,OAAO,KAAK;IAChB;;;;;OAKG;IACH,MAAM,CAAU,MAAM,GAAG,IAAI,cAAc,CAAqB,4BAA4B,EAAE;QAC5F,OAAO,EAAE,GAAG,EAAE,CACZ,UAAU,CACR,iCAAiC,EACjC,GAAG,EAAE,CACF,OAAO,CAAC,cAAc,CAAmC,CAAC,mBAAmB,EAAE,GAAG,CACjF,mBAAmB,CACpB;YACC,CAAC,CAAE,OAAO,CAAC,iCAAiC,CAAiB;YAC7D,CAAC,CAAC,IAAI,EACV,CAAC,KAAK,CAAC,CACR;KACJ,CAAC,CAAC;IAEc,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC9B,QAAQ,GAAG,IAAI,GAAG,EAAuB,CAAC;IAC1C,MAAM,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC;IACvC,SAAS,GAAG,KAAK,CAAC;IAClB,KAAK,GAAG,KAAK,CAAC;IACd,cAAc,GAAwB,IAAI,CAAC;IAClC,gBAAgB,GAAsD,EAAE,CAAC;IAEzE,WAAW,GAAG,MAAM,CAAC,KAAK;oFAAC,CAAC;IAC5B,cAAc,GAAG,MAAM,CAAC,KAAK;uFAAC,CAAC;IAC/B,cAAc,GAAG,MAAM,CAAC,KAAK;uFAAC,CAAC;IAC/B,YAAY,GAAG,MAAM,CAAsB,IAAI;qFAAC,CAAC;IACjD,YAAY,GAAG,MAAM,CAAsB,IAAI;qFAAC,CAAC;IACjD,aAAa,GAAG,MAAM,CAA0B,EAAE;sFAAC,CAAC;IACpD,UAAU,GAAG,MAAM,CAA0B,EAAE;mFAAC,CAAC;IACjD,cAAc,GAAG,MAAM,CAAsC,IAAI,GAAG,EAAE;uFAAC,CAAC;IACxE,UAAU,GAAG,MAAM,CAAoB,IAAI;mFAAC,CAAC;IAErD,SAAS,GAAG,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC;IACjC,MAAM,GAAoB,IAAI,CAAC,WAAW,CAAC,UAAU,EAAE,CAAC;IACxD,SAAS,GAAoB,IAAI,CAAC,cAAc,CAAC,UAAU,EAAE,CAAC;IAC9D,SAAS,GAAoB,IAAI,CAAC,cAAc,CAAC,UAAU,EAAE,CAAC;IAC9D,OAAO,GAAgC,IAAI,CAAC,YAAY,CAAC,UAAU,EAAE,CAAC;IACtE,OAAO,GAAgC,IAAI,CAAC,YAAY,CAAC,UAAU,EAAE,CAAC;IACtE,QAAQ,GAAoC,IAAI,CAAC,aAAa,CAAC,UAAU,EAAE,CAAC;IAC5E,KAAK,GAAoC,IAAI,CAAC,UAAU,CAAC,UAAU,EAAE,CAAC;IACtE,SAAS,GAChB,IAAI,CAAC,cAAc,CAAC,UAAU,EAAE,CAAC;IAC1B,KAAK,GAA8B,IAAI,CAAC,UAAU,CAAC,UAAU,EAAE,CAAC;IAEzE;QACE,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC3B,IAAI,CAAC,MAAM;YAAE,OAAO;QACpB,MAAM,MAAM,GAAG,MAAM,CAAC,WAAW,CAAC;QAClC,MAAM,KAAK,GAAkB;YAC3B,MAAM,CAAC,EAAE,CAAC,cAAc,EAAE,CAAC,SAAS,EAAE,EAAE;gBACtC,IAAI,SAAS;oBAAE,IAAI,CAAC,SAAS,EAAE,CAAC;;oBAC3B,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;gBACpC,KAAK,IAAI,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;YACrC,CAAC,CAAC;YACF,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,EAAE;gBAC7B,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;gBAC7B,6FAA6F;gBAC7F,IAAI,MAAM;oBAAE,IAAI,CAAC,SAAS,EAAE,CAAC;YAC/B,CAAC,CAAC;YACF,MAAM,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC,SAAS,EAAE,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;YACzE,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;YACvE,MAAM,CAAC,EAAE,CAAC,qBAAqB,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YAC7E,MAAM,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC,QAAQ,EAAE,EAAE,CAClC,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,GAAG,QAAQ,EAAE,GAAG,QAAQ,CAAC,CAAC,CACpE;YACD,MAAM,CAAC,EAAE,CAAC,eAAe,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YACjE,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAC1B,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,IAAI,GAAG,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC,CACnF;YACD,MAAM,CAAC,EAAE,CAAC,yBAAyB,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YAC1E,MAAM,CAAC,EAAE,CAAC,wBAAwB,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YACzE,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE,EAAE,CAC9B,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,CACtE;SACF,CAAC;QACF,MAAM,CAAC,UAAU,CAAC,CAAC,SAAS,CAAC,GAAG,EAAE;YAChC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;YACtB,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;QAClC,CAAC,CAAC,CAAC;QACH,KAAK,IAAI,CAAC,MAAM,EAAE,CAAC;QACnB,MAAM,CAAC,qBAAqB,EAAE,CAAC,IAAI,CACjC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,IAAI,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,EACtD,GAAG,EAAE,GAAE,CAAC,CACT,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,MAAM;QACV,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC3B,IAAI,CAAC,MAAM;YAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;QAC1E,MAAM,CAAC,MAAM,EAAE,SAAS,EAAE,SAAS,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YACvD,MAAM,CAAC,WAAW,EAAE;YACpB,MAAM,CAAC,sBAAsB,EAAE;YAC/B,MAAM,CAAC,eAAe,EAAE;SACzB,CAAC,CAAC;QACH,IAAI,SAAS,EAAE,CAAC;YACd,IAAI,CAAC,SAAS,EAAE,CAAC;YACjB,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;QACtD,CAAC;QACD,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC7B,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACnC,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAC/B,IAAI,MAAM;YAAE,IAAI,CAAC,SAAS,EAAE,CAAC;QAC7B,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC;IAC1C,CAAC;IAED,IAAI,CAAC,OAAqB;QACxB,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC3B,IAAI,CAAC,MAAM;YAAE,OAAO,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC;QAClD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CACrC,MAAM,CAAC,WAAW,CAChB,OAAO,EACP,CAAC,QAAQ,EAAE,EAAE;YACX,IAAI,CAAC,SAAS,EAAE,CAAC;YACjB,OAAO,CAAC,QAAQ,CAAC,CAAC;QACpB,CAAC,EACD,CAAC,KAAK,EAAE,EAAE;YACR,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YAC/B,MAAM,CAAC,KAAK,CAAC,CAAC;QAChB,CAAC,CACF,CACF,CAAC;IACJ,CAAC;IAED,QAAQ,CAAC,IAAY;QACnB,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC;IACzF,CAAC;IAED,SAAS,CAAC,OAA4B;QACpC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC3B,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC7C,CAAC;IAED,MAAM,CAAC,OAAqB;QAC1B,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,OAAO;QACzB,IAAI,IAAI,CAAC,KAAK;YAAE,IAAI,CAAC,MAAM,CAAC,wBAAwB,CAAC,OAAO,CAAC,CAAC;;YACzD,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC;IACrC,CAAC;IAED,cAAc;QACZ,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,qBAAqB,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACnF,CAAC;IAED,QAAQ,CAAC,QAAsB;QAC7B,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IAC9B,CAAC;IAED,oBAAoB,CAAC,QAAsB;QACzC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IAC7B,CAAC;IAED,QAAQ,CAAC,GAAW,EAAE,WAAyB,EAAE;QAC/C,OAAO,IAAI,CAAC,MAAM;YAChB,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC,GAAG,EAAE,QAAQ,CAAC;YAC9C,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC;IACpC,CAAC;IAEO,KAAK,CAAC,YAAqB,EAAE,QAAsB;QACzD,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC3B,IAAI,CAAC,MAAM;YAAE,OAAO;QACpB,IAAI,CAAC,IAAI,CAAC,KAAK;YAAE,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC,CAAC;aACjE,IAAI,YAAY;YAAE,MAAM,CAAC,mCAAmC,CAAC,QAAQ,CAAC,CAAC;;YACvE,MAAM,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IACzC,CAAC;IAEO,SAAS;QACf,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC3B,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC9B,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC9B,IAAI,CAAC,SAAS,EAAE,CAAC;IACnB,CAAC;IAEO,SAAS;QACf,IAAI,IAAI,CAAC,KAAK;YAAE,OAAO;QACvB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QAClB,IAAI,IAAI,CAAC,cAAc;YAAE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAC1D,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;QAC3B,KAAK,MAAM,CAAC,YAAY,EAAE,QAAQ,CAAC,IAAI,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;YACvE,IAAI,CAAC,KAAK,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC;QACrC,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,MAAM;QAClB,KAAK,MAAM,KAAK,IAAI,aAAa,EAAE,CAAC;YAClC,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;YAC3D,IAAI,IAAI,CAAC,SAAS;gBAAE,OAAO;YAC3B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;YACrD,IAAI,MAAM,EAAE,MAAM;gBAAE,OAAO;QAC7B,CAAC;IACH,CAAC;IAEO,QAAQ,CAAC,OAAqB,EAAE,KAAY;QAClD,IAAI,CAAC,SAAS,EAAE,CAAC;QACjB,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC/B,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACpC,KAAK,OAAO,CAAC,OAAO,EAAE;qBACnB,IAAI,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;qBAC5B,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC;YAC/D,CAAC;YACD,OAAO;QACT,CAAC;QACD,KAAK,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,KAAc,EAAE,EAAE;YACvD,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;YAC/B,KAAK,CAAC,EAAE,CAAC,CAAC;QACZ,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,KAAK,CAAC,MAAM,CAAC,OAAqB;QACxC,IAAI,QAAQ,GAAiB,EAAE,CAAC;QAChC,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YACpC,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;YACtC,IAAI,MAAM;gBAAE,QAAQ,GAAG,EAAE,GAAG,QAAQ,EAAE,GAAG,MAAM,EAAE,CAAC;QACpD,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;uGA7NU,KAAK;wGAAL,KAAK;;2FAAL,KAAK;kBADjB,OAAO;;AAiOR,SAAS,WAAW;IAClB,OAAO,IAAI,KAAK,CACd,sGAAsG,CACvG,CAAC;AACJ,CAAC","sourcesContent":["import {\n DestroyRef,\n ErrorHandler,\n InjectionToken,\n Service,\n inject,\n signal,\n type Signal,\n} from '@angular/core';\nimport { expoModule } from './native.ts';\n\nexport type WatchPayload = Record<string, unknown>;\n\nexport interface WatchFileTransfer {\n readonly id: string;\n readonly uri: string;\n readonly metadata: WatchPayload;\n readonly bytesTotal: number;\n readonly bytesTransferred: number;\n readonly fractionCompleted: number;\n readonly startTime: Date;\n readonly endTime: Date | null;\n readonly error: Error | null;\n}\n\nexport interface WatchFileEvent extends WatchFileTransfer {\n readonly type: string;\n}\n\nexport interface WatchError {\n readonly kind:\n | 'activation'\n | 'application-context'\n | 'application-context-received'\n | 'user-info'\n | 'file-received';\n readonly payload: unknown;\n}\n\ntype Unsubscribe = () => void;\ntype Reply = ((response: WatchPayload) => void) | null;\n\nexport interface NativeWatch {\n sendMessage(\n message: WatchPayload,\n reply?: (response: WatchPayload) => void,\n error?: (error: Error) => void,\n ): void;\n sendMessageData(data: string): Promise<string>;\n updateApplicationContext(context: WatchPayload): void;\n getApplicationContext(): Promise<WatchPayload | null>;\n transferUserInfo(userInfo: WatchPayload): void;\n transferCurrentComplicationUserInfo(userInfo: WatchPayload): void;\n startFileTransfer(uri: string, metadata?: WatchPayload): Promise<string>;\n getReachability(): Promise<boolean>;\n getIsPaired(): Promise<boolean>;\n getIsWatchAppInstalled(): Promise<boolean>;\n watchEvents: {\n on(event: 'message', listener: (message: WatchPayload, reply: Reply) => void): Unsubscribe;\n on(event: 'application-context', listener: (context: WatchPayload) => void): Unsubscribe;\n on(event: 'user-info', listener: (userInfo: WatchPayload[]) => void): Unsubscribe;\n on(event: 'file', listener: (event: WatchFileEvent) => void): Unsubscribe;\n on(event: 'file-received', listener: (files: WatchPayload[]) => void): Unsubscribe;\n on(\n event: 'reachability' | 'paired' | 'installed',\n listener: (value: boolean) => void,\n ): Unsubscribe;\n on(\n event:\n | 'activation-error'\n | 'application-context-error'\n | 'application-context-received-error'\n | 'user-info-error'\n | 'file-received-error'\n | 'session-became-inactive'\n | 'session-did-deactivate',\n listener: (payload: unknown) => void,\n ): Unsubscribe;\n };\n}\n\nexport type WatchMessageHandler = (\n message: WatchPayload,\n) => WatchPayload | void | Promise<WatchPayload | void>;\n\nconst ERRORS = [\n ['activation-error', 'activation'],\n ['application-context-error', 'application-context'],\n ['application-context-received-error', 'application-context-received'],\n ['user-info-error', 'user-info'],\n ['file-received-error', 'file-received'],\n] as const;\n\nconst SETTLE_DELAYS = [0, 250, 1000, 3000, 10000];\n\n@Service()\nexport class Watch {\n /**\n * `react-native-watch-connectivity`, iOS only. It is a TurboModule, which it asks for with\n * `getEnforcing` as it is evaluated, so it is evaluated only once `WatchConnectivity` is\n * registered: in Expo Go, or a build made before it was installed, it would throw an invariant\n * Metro reports as fatal rather than the `MissingModuleError` this answers.\n */\n static readonly SOURCE = new InjectionToken<NativeWatch | null>('angular-native.watchSource', {\n factory: () =>\n expoModule(\n 'react-native-watch-connectivity',\n () =>\n (require('react-native') as typeof import('react-native')).TurboModuleRegistry?.get(\n 'WatchConnectivity',\n )\n ? (require('react-native-watch-connectivity') as NativeWatch)\n : null,\n ['ios'],\n ),\n });\n\n private readonly native = inject(Watch.SOURCE);\n private readonly handlers = new Set<WatchMessageHandler>();\n private readonly errors = inject(ErrorHandler);\n private destroyed = false;\n private ready = false;\n private pendingContext: WatchPayload | null = null;\n private readonly pendingTransfers: [complication: boolean, userInfo: WatchPayload][] = [];\n\n private readonly pairedState = signal(false);\n private readonly installedState = signal(false);\n private readonly reachableState = signal(false);\n private readonly messageState = signal<WatchPayload | null>(null);\n private readonly contextState = signal<WatchPayload | null>(null);\n private readonly userInfoState = signal<readonly WatchPayload[]>([]);\n private readonly filesState = signal<readonly WatchPayload[]>([]);\n private readonly transfersState = signal<ReadonlyMap<string, WatchFileEvent>>(new Map());\n private readonly errorState = signal<WatchError | null>(null);\n\n readonly available = this.native !== null;\n readonly paired: Signal<boolean> = this.pairedState.asReadonly();\n readonly installed: Signal<boolean> = this.installedState.asReadonly();\n readonly reachable: Signal<boolean> = this.reachableState.asReadonly();\n readonly message: Signal<WatchPayload | null> = this.messageState.asReadonly();\n readonly context: Signal<WatchPayload | null> = this.contextState.asReadonly();\n readonly userInfo: Signal<readonly WatchPayload[]> = this.userInfoState.asReadonly();\n readonly files: Signal<readonly WatchPayload[]> = this.filesState.asReadonly();\n readonly transfers: Signal<ReadonlyMap<string, WatchFileEvent>> =\n this.transfersState.asReadonly();\n readonly error: Signal<WatchError | null> = this.errorState.asReadonly();\n\n constructor() {\n const native = this.native;\n if (!native) return;\n const events = native.watchEvents;\n const stops: Unsubscribe[] = [\n events.on('reachability', (reachable) => {\n if (reachable) this.connected();\n else this.reachableState.set(false);\n void this.status().catch(() => {});\n }),\n events.on('paired', (paired) => {\n this.pairedState.set(paired);\n // A watch paired after `settle` stopped asking: the session is active, so send what it held.\n if (paired) this.activated();\n }),\n events.on('installed', (installed) => this.installedState.set(installed)),\n events.on('message', (message, reply) => this.received(message, reply)),\n events.on('application-context', (context) => this.contextState.set(context)),\n events.on('user-info', (userInfo) =>\n this.userInfoState.update((received) => [...received, ...userInfo]),\n ),\n events.on('file-received', (files) => this.filesState.set(files)),\n events.on('file', (event) =>\n this.transfersState.update((transfers) => new Map(transfers).set(event.id, event)),\n ),\n events.on('session-became-inactive', () => this.reachableState.set(false)),\n events.on('session-did-deactivate', () => this.reachableState.set(false)),\n ...ERRORS.map(([event, kind]) =>\n events.on(event, (payload) => this.errorState.set({ kind, payload })),\n ),\n ];\n inject(DestroyRef).onDestroy(() => {\n this.destroyed = true;\n stops.forEach((stop) => stop());\n });\n void this.settle();\n native.getApplicationContext().then(\n (context) => context && this.contextState.set(context),\n () => {},\n );\n }\n\n async status(): Promise<{ paired: boolean; installed: boolean; reachable: boolean }> {\n const native = this.native;\n if (!native) return { paired: false, installed: false, reachable: false };\n const [paired, installed, reachable] = await Promise.all([\n native.getIsPaired(),\n native.getIsWatchAppInstalled(),\n native.getReachability(),\n ]);\n if (reachable) {\n this.connected();\n return { paired: true, installed: true, reachable };\n }\n this.pairedState.set(paired);\n this.installedState.set(installed);\n this.reachableState.set(false);\n if (paired) this.activated();\n return { paired, installed, reachable };\n }\n\n send(message: WatchPayload): Promise<WatchPayload> {\n const native = this.native;\n if (!native) return Promise.reject(unavailable());\n return new Promise((resolve, reject) =>\n native.sendMessage(\n message,\n (response) => {\n this.connected();\n resolve(response);\n },\n (error) => {\n this.reachableState.set(false);\n reject(error);\n },\n ),\n );\n }\n\n sendData(data: string): Promise<string> {\n return this.native ? this.native.sendMessageData(data) : Promise.reject(unavailable());\n }\n\n onMessage(handler: WatchMessageHandler): Unsubscribe {\n this.handlers.add(handler);\n return () => this.handlers.delete(handler);\n }\n\n update(context: WatchPayload): void {\n if (!this.native) return;\n if (this.ready) this.native.updateApplicationContext(context);\n else this.pendingContext = context;\n }\n\n currentContext(): Promise<WatchPayload | null> {\n return this.native ? this.native.getApplicationContext() : Promise.resolve(null);\n }\n\n transfer(userInfo: WatchPayload): void {\n this.queue(false, userInfo);\n }\n\n transferComplication(userInfo: WatchPayload): void {\n this.queue(true, userInfo);\n }\n\n sendFile(uri: string, metadata: WatchPayload = {}): Promise<string> {\n return this.native\n ? this.native.startFileTransfer(uri, metadata)\n : Promise.reject(unavailable());\n }\n\n private queue(complication: boolean, userInfo: WatchPayload): void {\n const native = this.native;\n if (!native) return;\n if (!this.ready) this.pendingTransfers.push([complication, userInfo]);\n else if (complication) native.transferCurrentComplicationUserInfo(userInfo);\n else native.transferUserInfo(userInfo);\n }\n\n private connected(): void {\n this.pairedState.set(true);\n this.installedState.set(true);\n this.reachableState.set(true);\n this.activated();\n }\n\n private activated(): void {\n if (this.ready) return;\n this.ready = true;\n if (this.pendingContext) this.update(this.pendingContext);\n this.pendingContext = null;\n for (const [complication, userInfo] of this.pendingTransfers.splice(0)) {\n this.queue(complication, userInfo);\n }\n }\n\n private async settle(): Promise<void> {\n for (const delay of SETTLE_DELAYS) {\n await new Promise((resolve) => setTimeout(resolve, delay));\n if (this.destroyed) return;\n const status = await this.status().catch(() => null);\n if (status?.paired) return;\n }\n }\n\n private received(message: WatchPayload, reply: Reply): void {\n this.connected();\n this.messageState.set(message);\n if (!reply) {\n for (const handler of this.handlers) {\n void Promise.resolve()\n .then(() => handler(message))\n .catch((error: unknown) => this.errors.handleError(error));\n }\n return;\n }\n void this.answer(message).then(reply, (error: unknown) => {\n this.errors.handleError(error);\n reply({});\n });\n }\n\n private async answer(message: WatchPayload): Promise<WatchPayload> {\n let response: WatchPayload = {};\n for (const handler of this.handlers) {\n const result = await handler(message);\n if (result) response = { ...response, ...result };\n }\n return response;\n }\n}\n\nfunction unavailable(): Error {\n return new Error(\n 'No paired Apple Watch session: react-native-watch-connectivity is not installed, or this is not iOS.',\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"watch.js","sourceRoot":"","sources":["../src/watch.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n DestroyRef,\n ErrorHandler,\n InjectionToken,\n Service,\n inject,\n signal,\n type Signal,\n} from '@angular/core';\nimport { expoModule, present } from './native.ts';\n\nexport type WatchPayload = Record<string, unknown>;\n\nexport interface WatchFileTransfer {\n readonly id: string;\n readonly uri: string;\n readonly metadata: WatchPayload;\n readonly bytesTotal: number;\n readonly bytesTransferred: number;\n readonly fractionCompleted: number;\n readonly startTime: Date;\n readonly endTime: Date | null;\n readonly error: Error | null;\n}\n\nexport interface WatchFileEvent extends WatchFileTransfer {\n readonly type: string;\n}\n\nexport interface WatchError {\n readonly kind:\n | 'activation'\n | 'application-context'\n | 'application-context-received'\n | 'user-info'\n | 'file-received';\n readonly payload: unknown;\n}\n\ntype Unsubscribe = () => void;\ntype Reply = ((response: WatchPayload) => void) | null;\n\nexport interface NativeWatch {\n sendMessage(\n message: WatchPayload,\n reply?: (response: WatchPayload) => void,\n error?: (error: Error) => void,\n ): void;\n sendMessageData(data: string): Promise<string>;\n updateApplicationContext(context: WatchPayload): void;\n getApplicationContext(): Promise<WatchPayload | null>;\n transferUserInfo(userInfo: WatchPayload): void;\n transferCurrentComplicationUserInfo(userInfo: WatchPayload): void;\n startFileTransfer(uri: string, metadata?: WatchPayload): Promise<string>;\n getReachability(): Promise<boolean>;\n getIsPaired(): Promise<boolean>;\n getIsWatchAppInstalled(): Promise<boolean>;\n watchEvents: {\n on(event: 'message', listener: (message: WatchPayload, reply: Reply) => void): Unsubscribe;\n on(event: 'application-context', listener: (context: WatchPayload) => void): Unsubscribe;\n on(event: 'user-info', listener: (userInfo: WatchPayload[]) => void): Unsubscribe;\n on(event: 'file', listener: (event: WatchFileEvent) => void): Unsubscribe;\n on(event: 'file-received', listener: (files: WatchPayload[]) => void): Unsubscribe;\n on(\n event: 'reachability' | 'paired' | 'installed',\n listener: (value: boolean) => void,\n ): Unsubscribe;\n on(\n event:\n | 'activation-error'\n | 'application-context-error'\n | 'application-context-received-error'\n | 'user-info-error'\n | 'file-received-error'\n | 'session-became-inactive'\n | 'session-did-deactivate',\n listener: (payload: unknown) => void,\n ): Unsubscribe;\n };\n}\n\nexport type WatchMessageHandler = (\n message: WatchPayload,\n) => WatchPayload | void | Promise<WatchPayload | void>;\n\nconst ERRORS = [\n ['activation-error', 'activation'],\n ['application-context-error', 'application-context'],\n ['application-context-received-error', 'application-context-received'],\n ['user-info-error', 'user-info'],\n ['file-received-error', 'file-received'],\n] as const;\n\nconst SETTLE_DELAYS = [0, 250, 1000, 3000, 10000];\n\n@Service()\nexport class Watch {\n /**\n * `react-native-watch-connectivity`, iOS only. It is a TurboModule, which it asks for with\n * `getEnforcing` as it is evaluated, so it is evaluated only once `WatchConnectivity` is\n * registered: in Expo Go, or a build made before it was installed, it would throw an invariant\n * Metro reports as fatal rather than the `MissingModuleError` this answers.\n */\n static readonly SOURCE = new InjectionToken<NativeWatch | null>('angular-native.watchSource', {\n factory: () =>\n expoModule(\n 'react-native-watch-connectivity',\n () =>\n (require('react-native') as typeof import('react-native')).TurboModuleRegistry?.get(\n 'WatchConnectivity',\n )\n ? (require('react-native-watch-connectivity') as NativeWatch)\n : null,\n ['ios'],\n ),\n });\n\n private readonly native = inject(Watch.SOURCE);\n private readonly handlers = new Set<WatchMessageHandler>();\n private readonly errors = inject(ErrorHandler);\n private destroyed = false;\n private ready = false;\n private pendingContext: WatchPayload | null = null;\n private readonly pendingTransfers: [complication: boolean, userInfo: WatchPayload][] = [];\n\n private readonly pairedState = signal(false);\n private readonly installedState = signal(false);\n private readonly reachableState = signal(false);\n private readonly messageState = signal<WatchPayload | null>(null);\n private readonly contextState = signal<WatchPayload | null>(null);\n private readonly userInfoState = signal<readonly WatchPayload[]>([]);\n private readonly filesState = signal<readonly WatchPayload[]>([]);\n private readonly transfersState = signal<ReadonlyMap<string, WatchFileEvent>>(new Map());\n private readonly errorState = signal<WatchError | null>(null);\n\n readonly available = present(this.native) !== null;\n readonly paired: Signal<boolean> = this.pairedState.asReadonly();\n readonly installed: Signal<boolean> = this.installedState.asReadonly();\n readonly reachable: Signal<boolean> = this.reachableState.asReadonly();\n readonly message: Signal<WatchPayload | null> = this.messageState.asReadonly();\n readonly context: Signal<WatchPayload | null> = this.contextState.asReadonly();\n readonly userInfo: Signal<readonly WatchPayload[]> = this.userInfoState.asReadonly();\n readonly files: Signal<readonly WatchPayload[]> = this.filesState.asReadonly();\n readonly transfers: Signal<ReadonlyMap<string, WatchFileEvent>> =\n this.transfersState.asReadonly();\n readonly error: Signal<WatchError | null> = this.errorState.asReadonly();\n\n constructor() {\n const native = present(this.native);\n if (!native) return;\n const events = native.watchEvents;\n const stops: Unsubscribe[] = [\n events.on('reachability', (reachable) => {\n if (reachable) this.connected();\n else this.reachableState.set(false);\n void this.status().catch(() => {});\n }),\n events.on('paired', (paired) => {\n this.pairedState.set(paired);\n // A watch paired after `settle` stopped asking: the session is active, so send what it held.\n if (paired) this.activated();\n }),\n events.on('installed', (installed) => this.installedState.set(installed)),\n events.on('message', (message, reply) => this.received(message, reply)),\n events.on('application-context', (context) => this.contextState.set(context)),\n events.on('user-info', (userInfo) =>\n this.userInfoState.update((received) => [...received, ...userInfo]),\n ),\n events.on('file-received', (files) => this.filesState.set(files)),\n events.on('file', (event) =>\n this.transfersState.update((transfers) => new Map(transfers).set(event.id, event)),\n ),\n events.on('session-became-inactive', () => this.reachableState.set(false)),\n events.on('session-did-deactivate', () => this.reachableState.set(false)),\n ...ERRORS.map(([event, kind]) =>\n events.on(event, (payload) => this.errorState.set({ kind, payload })),\n ),\n ];\n inject(DestroyRef).onDestroy(() => {\n this.destroyed = true;\n stops.forEach((stop) => stop());\n });\n void this.settle();\n native.getApplicationContext().then(\n (context) => context && this.contextState.set(context),\n () => {},\n );\n }\n\n async status(): Promise<{ paired: boolean; installed: boolean; reachable: boolean }> {\n const native = this.native;\n if (!native) return { paired: false, installed: false, reachable: false };\n const [paired, installed, reachable] = await Promise.all([\n native.getIsPaired(),\n native.getIsWatchAppInstalled(),\n native.getReachability(),\n ]);\n if (reachable) {\n this.connected();\n return { paired: true, installed: true, reachable };\n }\n this.pairedState.set(paired);\n this.installedState.set(installed);\n this.reachableState.set(false);\n if (paired) this.activated();\n return { paired, installed, reachable };\n }\n\n send(message: WatchPayload): Promise<WatchPayload> {\n const native = this.native;\n if (!native) return Promise.reject(unavailable());\n return new Promise((resolve, reject) =>\n native.sendMessage(\n message,\n (response) => {\n this.connected();\n resolve(response);\n },\n (error) => {\n this.reachableState.set(false);\n reject(error);\n },\n ),\n );\n }\n\n sendData(data: string): Promise<string> {\n return this.native ? this.native.sendMessageData(data) : Promise.reject(unavailable());\n }\n\n onMessage(handler: WatchMessageHandler): Unsubscribe {\n this.handlers.add(handler);\n return () => this.handlers.delete(handler);\n }\n\n update(context: WatchPayload): void {\n if (!this.native) return;\n // Missing from the build, the stand-in is called rather than held for later: it says so.\n if (this.ready || !present(this.native)) this.native.updateApplicationContext(context);\n else this.pendingContext = context;\n }\n\n currentContext(): Promise<WatchPayload | null> {\n return this.native ? this.native.getApplicationContext() : Promise.resolve(null);\n }\n\n transfer(userInfo: WatchPayload): void {\n this.queue(false, userInfo);\n }\n\n transferComplication(userInfo: WatchPayload): void {\n this.queue(true, userInfo);\n }\n\n sendFile(uri: string, metadata: WatchPayload = {}): Promise<string> {\n return this.native\n ? this.native.startFileTransfer(uri, metadata)\n : Promise.reject(unavailable());\n }\n\n private queue(complication: boolean, userInfo: WatchPayload): void {\n const native = this.native;\n if (!native) return;\n if (!this.ready && present(native)) this.pendingTransfers.push([complication, userInfo]);\n else if (complication) native.transferCurrentComplicationUserInfo(userInfo);\n else native.transferUserInfo(userInfo);\n }\n\n private connected(): void {\n this.pairedState.set(true);\n this.installedState.set(true);\n this.reachableState.set(true);\n this.activated();\n }\n\n private activated(): void {\n if (this.ready) return;\n this.ready = true;\n if (this.pendingContext) this.update(this.pendingContext);\n this.pendingContext = null;\n for (const [complication, userInfo] of this.pendingTransfers.splice(0)) {\n this.queue(complication, userInfo);\n }\n }\n\n private async settle(): Promise<void> {\n for (const delay of SETTLE_DELAYS) {\n await new Promise((resolve) => setTimeout(resolve, delay));\n if (this.destroyed) return;\n const status = await this.status().catch(() => null);\n if (status?.paired) return;\n }\n }\n\n private received(message: WatchPayload, reply: Reply): void {\n this.connected();\n this.messageState.set(message);\n if (!reply) {\n for (const handler of this.handlers) {\n void Promise.resolve()\n .then(() => handler(message))\n .catch((error: unknown) => this.errors.handleError(error));\n }\n return;\n }\n void this.answer(message).then(reply, (error: unknown) => {\n this.errors.handleError(error);\n reply({});\n });\n }\n\n private async answer(message: WatchPayload): Promise<WatchPayload> {\n let response: WatchPayload = {};\n for (const handler of this.handlers) {\n const result = await handler(message);\n if (result) response = { ...response, ...result };\n }\n return response;\n }\n}\n\nfunction unavailable(): Error {\n return new Error(\n 'No paired Apple Watch session: react-native-watch-connectivity is not installed, or this is not iOS.',\n );\n}\n"]}
|
package/expo-module.config.json
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"platforms": ["apple", "android"],
|
|
3
3
|
"apple": {
|
|
4
|
-
"modules": ["LiveActivityTapsModule"],
|
|
4
|
+
"modules": ["LiveActivityTapsModule", "ExpoUIModifiersModule", "SearchFieldModule"],
|
|
5
5
|
"appDelegateSubscribers": ["LiveActivityTapsObserver"]
|
|
6
6
|
},
|
|
7
7
|
"android": {
|
|
8
|
-
"modules": [
|
|
8
|
+
"modules": [
|
|
9
|
+
"dev.ngnative.expo.OngoingNotificationModule",
|
|
10
|
+
"dev.ngnative.expo.SearchFieldModule"
|
|
11
|
+
]
|
|
9
12
|
}
|
|
10
13
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import ExpoModulesCore
|
|
2
|
+
#if canImport(ExpoUI)
|
|
3
|
+
import ExpoUI
|
|
4
|
+
#endif
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
The SwiftUI modifiers `@ng-native/expo` adds to `@expo/ui`'s, registered as it loads. A build
|
|
8
|
+
without `@expo/ui` has no modifiers to add to, and this does nothing.
|
|
9
|
+
*/
|
|
10
|
+
public final class ExpoUIModifiersModule: Module {
|
|
11
|
+
public func definition() -> ModuleDefinition {
|
|
12
|
+
Name("NgNativeExpoUIModifiers")
|
|
13
|
+
|
|
14
|
+
OnCreate {
|
|
15
|
+
#if canImport(ExpoUI)
|
|
16
|
+
ViewModifierRegistry.register("ngNativeInlineNavigationTitle") { _, _, _ in
|
|
17
|
+
InlineNavigationTitle()
|
|
18
|
+
}
|
|
19
|
+
ViewModifierRegistry.register("ngNativeSegmentedFont") { params, _, _ in
|
|
20
|
+
SegmentedFont(params: params)
|
|
21
|
+
}
|
|
22
|
+
#endif
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import SwiftUI
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
Shows a navigation stack's title inline in its bar. `ui-bottom-sheet` puts it on the toolbar of a
|
|
5
|
+
sheet that has one: a navigation stack's root shows a large title by default, and SwiftUI leaves
|
|
6
|
+
room for one under the bar even with no title to show.
|
|
7
|
+
*/
|
|
8
|
+
struct InlineNavigationTitle: ViewModifier {
|
|
9
|
+
func body(content: Content) -> some View {
|
|
10
|
+
content.navigationBarTitleDisplayMode(.inline)
|
|
11
|
+
}
|
|
12
|
+
}
|
package/ios/NgNativeExpo.podspec
CHANGED
|
@@ -16,6 +16,9 @@ Pod::Spec.new do |s|
|
|
|
16
16
|
s.static_framework = true
|
|
17
17
|
|
|
18
18
|
s.dependency 'ExpoModulesCore'
|
|
19
|
+
# `@expo/ui` is an optional peer. Where the app has it, the modifiers it is given are built.
|
|
20
|
+
has_expo_ui = system('node', '-e', "require.resolve('@expo/ui/package.json', { paths: [process.argv[1]] })", __dir__, out: File::NULL, err: File::NULL)
|
|
21
|
+
s.dependency 'ExpoUI' if has_expo_ui
|
|
19
22
|
|
|
20
23
|
s.source_files = '**/*.swift'
|
|
21
24
|
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' }
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import ExpoModulesCore
|
|
2
|
+
import UIKit
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
`<search-field>`: UIKit's search bar as a view of its own, for a search field outside a navigation
|
|
6
|
+
bar. The text is controlled from JavaScript, and a text older than what has been typed since is
|
|
7
|
+
skipped, as React Native's text input skips one.
|
|
8
|
+
*/
|
|
9
|
+
public final class SearchFieldModule: Module {
|
|
10
|
+
public func definition() -> ModuleDefinition {
|
|
11
|
+
Name("NgNativeSearchField")
|
|
12
|
+
|
|
13
|
+
View(SearchFieldView.self) {
|
|
14
|
+
Events("onChangeText", "onSubmitSearch", "onPressCancel", "onBeginEditing", "onEndEditing")
|
|
15
|
+
|
|
16
|
+
Prop("text") { (view: SearchFieldView, text: String?) in
|
|
17
|
+
view.text = text ?? ""
|
|
18
|
+
}
|
|
19
|
+
Prop("mostRecentEventCount") { (view: SearchFieldView, count: Int?) in
|
|
20
|
+
view.mostRecentEventCount = count ?? 0
|
|
21
|
+
}
|
|
22
|
+
Prop("enabled") { (view: SearchFieldView, enabled: Bool?) in
|
|
23
|
+
let enabled = enabled ?? true
|
|
24
|
+
// The text field and the Cancel button: disabled while it is edited, Cancel would still
|
|
25
|
+
// empty the field.
|
|
26
|
+
view.searchBar.isUserInteractionEnabled = enabled
|
|
27
|
+
view.searchBar.searchTextField.isEnabled = enabled
|
|
28
|
+
view.searchBar.alpha = enabled ? 1 : 0.5
|
|
29
|
+
if !enabled { view.searchBar.resignFirstResponder() }
|
|
30
|
+
}
|
|
31
|
+
Prop("placeholder") { (view: SearchFieldView, placeholder: String?) in
|
|
32
|
+
view.searchBar.placeholder = placeholder
|
|
33
|
+
}
|
|
34
|
+
Prop("showsCancelButton") { (view: SearchFieldView, shows: Bool?) in
|
|
35
|
+
view.showsCancelButton = shows
|
|
36
|
+
}
|
|
37
|
+
Prop("autoCapitalize") { (view: SearchFieldView, value: String?) in
|
|
38
|
+
view.searchBar.autocapitalizationType = switch value {
|
|
39
|
+
case "words": .words
|
|
40
|
+
case "sentences": .sentences
|
|
41
|
+
case "characters": .allCharacters
|
|
42
|
+
default: .none
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
Prop("tintColor") { (view: SearchFieldView, color: UIColor?) in
|
|
46
|
+
view.searchBar.tintColor = color
|
|
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
|
+
}
|
|
62
|
+
|
|
63
|
+
AsyncFunction("focus") { (view: SearchFieldView) in
|
|
64
|
+
view.searchBar.becomeFirstResponder()
|
|
65
|
+
}.runOnQueue(.main)
|
|
66
|
+
AsyncFunction("blur") { (view: SearchFieldView) in
|
|
67
|
+
view.searchBar.resignFirstResponder()
|
|
68
|
+
}.runOnQueue(.main)
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
final class SearchFieldView: ExpoView, UISearchBarDelegate {
|
|
74
|
+
let searchBar = UISearchBar()
|
|
75
|
+
let onChangeText = EventDispatcher()
|
|
76
|
+
let onSubmitSearch = EventDispatcher()
|
|
77
|
+
let onPressCancel = EventDispatcher()
|
|
78
|
+
let onBeginEditing = EventDispatcher()
|
|
79
|
+
let onEndEditing = EventDispatcher()
|
|
80
|
+
|
|
81
|
+
/// Changes the user has made, counted; JavaScript sends back the count its text answers.
|
|
82
|
+
private var eventCount = 0
|
|
83
|
+
var mostRecentEventCount = 0 {
|
|
84
|
+
didSet { applyText() }
|
|
85
|
+
}
|
|
86
|
+
var text = "" {
|
|
87
|
+
didSet { applyText() }
|
|
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
|
+
}
|
|
103
|
+
/// Nil shows the Cancel button while the field has the keyboard.
|
|
104
|
+
var showsCancelButton: Bool? {
|
|
105
|
+
didSet { searchBar.setShowsCancelButton(showsCancelButton ?? searchBar.isFirstResponder, animated: false) }
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
required init(appContext: AppContext? = nil) {
|
|
109
|
+
super.init(appContext: appContext)
|
|
110
|
+
searchBar.searchBarStyle = .minimal
|
|
111
|
+
searchBar.delegate = self
|
|
112
|
+
addSubview(searchBar)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
override func layoutSubviews() {
|
|
116
|
+
super.layoutSubviews()
|
|
117
|
+
searchBar.frame = bounds
|
|
118
|
+
}
|
|
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
|
+
|
|
162
|
+
/// Puts JavaScript's text in the field, unless it answers a keystroke older than the last one.
|
|
163
|
+
private func applyText() {
|
|
164
|
+
guard mostRecentEventCount >= eventCount, searchBar.text != text else { return }
|
|
165
|
+
searchBar.text = text
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
private func changed(_ text: String) {
|
|
169
|
+
eventCount += 1
|
|
170
|
+
onChangeText(["text": text, "eventCount": eventCount])
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
|
|
174
|
+
changed(searchText)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
|
|
178
|
+
onSubmitSearch(["text": searchBar.text ?? ""])
|
|
179
|
+
searchBar.resignFirstResponder()
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
|
|
183
|
+
if !(searchBar.text ?? "").isEmpty {
|
|
184
|
+
searchBar.text = ""
|
|
185
|
+
changed("")
|
|
186
|
+
}
|
|
187
|
+
onPressCancel()
|
|
188
|
+
searchBar.resignFirstResponder()
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
|
|
192
|
+
if showsCancelButton == nil { searchBar.setShowsCancelButton(true, animated: true) }
|
|
193
|
+
onBeginEditing()
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {
|
|
197
|
+
if showsCancelButton == nil { searchBar.setShowsCancelButton(false, animated: true) }
|
|
198
|
+
onEndEditing()
|
|
199
|
+
}
|
|
200
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import SwiftUI
|
|
2
|
+
import UIKit
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
Draws a segmented picker's segments in a font. SwiftUI draws them as a `UISegmentedControl`, and
|
|
6
|
+
`font` does not reach it, so this finds the control and sets its title font. `ui-picker` sends it
|
|
7
|
+
beside each `font` modifier of a segmented picker, with the same `family`, `size` and `weight`.
|
|
8
|
+
*/
|
|
9
|
+
struct SegmentedFont: ViewModifier {
|
|
10
|
+
let font: UIFont
|
|
11
|
+
|
|
12
|
+
init(params: [String: Any]) {
|
|
13
|
+
let size = (params["size"] as? Double).map { CGFloat($0) } ?? UIFont.systemFontSize
|
|
14
|
+
let weight = Self.weight(params["weight"] as? String)
|
|
15
|
+
guard let family = params["family"] as? String else {
|
|
16
|
+
font = .systemFont(ofSize: size, weight: weight ?? .regular)
|
|
17
|
+
return
|
|
18
|
+
}
|
|
19
|
+
// A face's name or a family's, as `font` takes it, then the weight within that family.
|
|
20
|
+
let named = UIFont(name: family, size: size)
|
|
21
|
+
guard let weight else {
|
|
22
|
+
font = named ?? UIFont(descriptor: UIFontDescriptor(fontAttributes: [.family: family]), size: size)
|
|
23
|
+
return
|
|
24
|
+
}
|
|
25
|
+
let descriptor = UIFontDescriptor(fontAttributes: [
|
|
26
|
+
.family: named?.familyName ?? family,
|
|
27
|
+
.traits: [UIFontDescriptor.TraitKey.weight: weight],
|
|
28
|
+
])
|
|
29
|
+
font = UIFont(descriptor: descriptor, size: size)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
func body(content: Content) -> some View {
|
|
33
|
+
content.background(SegmentedFontSetter(font: font))
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/// `@expo/ui`'s font weights, as UIKit's.
|
|
37
|
+
private static func weight(_ name: String?) -> UIFont.Weight? {
|
|
38
|
+
switch name {
|
|
39
|
+
case "regular": return .regular
|
|
40
|
+
case "ultraLight": return .ultraLight
|
|
41
|
+
case "thin": return .thin
|
|
42
|
+
case "light": return .light
|
|
43
|
+
case "medium": return .medium
|
|
44
|
+
case "semibold": return .semibold
|
|
45
|
+
case "bold": return .bold
|
|
46
|
+
case "heavy": return .heavy
|
|
47
|
+
case "black": return .black
|
|
48
|
+
default: return nil
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/// A view beside the picker that finds its control, and gives it the system font back when it goes.
|
|
54
|
+
private struct SegmentedFontSetter: UIViewRepresentable {
|
|
55
|
+
let font: UIFont
|
|
56
|
+
|
|
57
|
+
func makeUIView(context: Context) -> SegmentedFontView { SegmentedFontView() }
|
|
58
|
+
|
|
59
|
+
func updateUIView(_ view: SegmentedFontView, context: Context) {
|
|
60
|
+
view.font = font
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
static func dismantleUIView(_ view: SegmentedFontView, coordinator: ()) {
|
|
64
|
+
view.font = nil
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
private final class SegmentedFontView: UIView {
|
|
69
|
+
private weak var control: UISegmentedControl?
|
|
70
|
+
|
|
71
|
+
var font: UIFont? {
|
|
72
|
+
didSet { font == nil ? apply(to: control) : applyOnceLaidOut() }
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
override func didMoveToWindow() {
|
|
76
|
+
super.didMoveToWindow()
|
|
77
|
+
if window != nil { applyOnceLaidOut() }
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/// SwiftUI adds the control beside this view in the same pass, so it is looked for after it.
|
|
81
|
+
private func applyOnceLaidOut() {
|
|
82
|
+
DispatchQueue.main.async { [weak self] in
|
|
83
|
+
guard let self, self.font != nil, self.window != nil else { return }
|
|
84
|
+
if self.control?.window == nil { self.control = self.nearestControl() }
|
|
85
|
+
self.apply(to: self.control)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
private func apply(to control: UISegmentedControl?) {
|
|
90
|
+
let attributes: [NSAttributedString.Key: Any]? = font.map { [.font: $0] }
|
|
91
|
+
control?.setTitleTextAttributes(attributes, for: .normal)
|
|
92
|
+
control?.setTitleTextAttributes(attributes, for: .selected)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// ponytail: the first control under the nearest ancestor that has one. The picker and this view
|
|
96
|
+
// share a parent, so that is the picker's own; walk SwiftUI's tree if two ever share one.
|
|
97
|
+
private func nearestControl() -> UISegmentedControl? {
|
|
98
|
+
var above = superview
|
|
99
|
+
while let view = above {
|
|
100
|
+
if let found = Self.firstControl(in: view) { return found }
|
|
101
|
+
above = view.superview
|
|
102
|
+
}
|
|
103
|
+
return nil
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
private static func firstControl(in view: UIView) -> UISegmentedControl? {
|
|
107
|
+
if let control = view as? UISegmentedControl { return control }
|
|
108
|
+
for subview in view.subviews {
|
|
109
|
+
if let control = firstControl(in: subview) { return control }
|
|
110
|
+
}
|
|
111
|
+
return nil
|
|
112
|
+
}
|
|
113
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ng-native/expo",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.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",
|
|
@@ -167,6 +167,10 @@
|
|
|
167
167
|
"types": "./dist/screen-capture.d.ts",
|
|
168
168
|
"default": "./dist/screen-capture.js"
|
|
169
169
|
},
|
|
170
|
+
"./search-field": {
|
|
171
|
+
"types": "./dist/search-field.d.ts",
|
|
172
|
+
"default": "./dist/search-field.js"
|
|
173
|
+
},
|
|
170
174
|
"./secure-store": {
|
|
171
175
|
"types": "./dist/secure-store.d.ts",
|
|
172
176
|
"default": "./dist/secure-store.js"
|
|
@@ -213,7 +217,7 @@
|
|
|
213
217
|
"access": "public"
|
|
214
218
|
},
|
|
215
219
|
"dependencies": {
|
|
216
|
-
"@ng-native/fabric": "0.
|
|
220
|
+
"@ng-native/fabric": "0.15.0"
|
|
217
221
|
},
|
|
218
222
|
"nx": {
|
|
219
223
|
"tags": [
|