@multiplatform.one/forms 7.7.6 → 7.10.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/dist/cjs/Button/index.cjs +1 -4
- package/dist/cjs/Button/index.native.js +5 -6
- package/dist/cjs/Button/index.native.js.map +1 -1
- package/dist/cjs/Chip/index.cjs +1 -1
- package/dist/cjs/Chip/index.native.js +1 -1
- package/dist/cjs/Chip/index.native.js.map +1 -1
- package/dist/cjs/FloatingPanel/index.cjs +59 -14
- package/dist/cjs/FloatingPanel/index.native.js +16 -7
- package/dist/cjs/FloatingPanel/index.native.js.map +1 -1
- package/dist/cjs/FloatingPanel/nestedAction.cjs +37 -0
- package/dist/cjs/FloatingPanel/nestedAction.native.js +42 -0
- package/dist/cjs/FloatingPanel/nestedAction.native.js.map +1 -0
- package/dist/cjs/InputParts/index.cjs +19 -8
- package/dist/cjs/InputParts/index.native.js +20 -11
- package/dist/cjs/InputParts/index.native.js.map +1 -1
- package/dist/cjs/Wheel/index.cjs +3 -3
- package/dist/cjs/Wheel/index.native.js +3 -3
- package/dist/cjs/Wheel/index.native.js.map +1 -1
- package/dist/cjs/fieldLayout.cjs +3 -3
- package/dist/cjs/fieldLayout.native.js +3 -3
- package/dist/cjs/fieldLayout.native.js.map +1 -1
- package/dist/cjs/fields/Checkbox/CheckboxFieldFrame.cjs +9 -2
- package/dist/cjs/fields/Checkbox/CheckboxFieldFrame.native.js +10 -1
- package/dist/cjs/fields/Checkbox/CheckboxFieldFrame.native.js.map +1 -1
- package/dist/cjs/fields/Combobox/index.cjs +36 -29
- package/dist/cjs/fields/Combobox/index.native.js +38 -38
- package/dist/cjs/fields/Combobox/index.native.js.map +1 -1
- package/dist/cjs/fields/DatePicker/Calendar.cjs +6 -1
- package/dist/cjs/fields/DatePicker/Calendar.native.js +6 -1
- package/dist/cjs/fields/DatePicker/Calendar.native.js.map +1 -1
- package/dist/cjs/fields/DatePicker/DateRangePicker.cjs +2 -2
- package/dist/cjs/fields/DatePicker/DateRangePicker.native.js +2 -2
- package/dist/cjs/fields/DatePicker/DateRangePicker.native.js.map +1 -1
- package/dist/cjs/fields/DatePicker/DatetimePicker.catalog.cjs +15 -12
- package/dist/cjs/fields/DatePicker/DatetimePicker.catalog.native.js +16 -12
- package/dist/cjs/fields/DatePicker/DatetimePicker.catalog.native.js.map +1 -1
- package/dist/cjs/fields/DatePicker/DatetimePicker.native.js +39 -14
- package/dist/cjs/fields/DatePicker/DatetimePicker.native.js.map +1 -1
- package/dist/cjs/fields/DatePicker/MonthPicker.cjs +10 -3
- package/dist/cjs/fields/DatePicker/MonthPicker.native.js +10 -3
- package/dist/cjs/fields/DatePicker/MonthPicker.native.js.map +1 -1
- package/dist/cjs/fields/DatePicker/MultiDatePicker.cjs +2 -2
- package/dist/cjs/fields/DatePicker/MultiDatePicker.native.js +2 -2
- package/dist/cjs/fields/DatePicker/MultiDatePicker.native.js.map +1 -1
- package/dist/cjs/fields/DatePicker/index.native.js +3 -0
- package/dist/cjs/fields/DatePicker/index.native.js.map +1 -1
- package/dist/cjs/fields/DatePicker/parts.cjs +26 -14
- package/dist/cjs/fields/DatePicker/parts.native.js +28 -14
- package/dist/cjs/fields/DatePicker/parts.native.js.map +1 -1
- package/dist/cjs/fields/FileUpload/index.cjs +6 -0
- package/dist/cjs/fields/FileUpload/index.native.js +6 -0
- package/dist/cjs/fields/FileUpload/index.native.js.map +1 -1
- package/dist/cjs/fields/Geolocation/GeolocationMap.native.js.map +1 -1
- package/dist/cjs/fields/Geolocation/geocoder.cjs +63 -0
- package/dist/cjs/fields/Geolocation/geocoder.native.js +68 -0
- package/dist/cjs/fields/Geolocation/geocoder.native.js.map +1 -0
- package/dist/cjs/fields/Geolocation/index.cjs +801 -152
- package/dist/cjs/fields/Geolocation/index.native.js +856 -167
- package/dist/cjs/fields/Geolocation/index.native.js.map +1 -1
- package/dist/cjs/fields/Geolocation/nativeSearchFocus.cjs +44 -0
- package/dist/cjs/fields/Geolocation/nativeSearchFocus.native.js +47 -0
- package/dist/cjs/fields/Geolocation/nativeSearchFocus.native.js.map +1 -0
- package/dist/cjs/fields/Icon/drawnSet.cjs +16 -3
- package/dist/cjs/fields/Icon/drawnSet.native.js +20 -3
- package/dist/cjs/fields/Icon/drawnSet.native.js.map +1 -1
- package/dist/cjs/fields/Icon/index.cjs +102 -27
- package/dist/cjs/fields/Icon/index.native.js +115 -31
- package/dist/cjs/fields/Icon/index.native.js.map +1 -1
- package/dist/cjs/fields/Input/index.cjs +5 -12
- package/dist/cjs/fields/Input/index.native.js +5 -13
- package/dist/cjs/fields/Input/index.native.js.map +1 -1
- package/dist/cjs/fields/PhoneInput/index.cjs +2 -2
- package/dist/cjs/fields/PhoneInput/index.native.js +2 -2
- package/dist/cjs/fields/PhoneInput/index.native.js.map +1 -1
- package/dist/cjs/fields/RadioGroup/index.cjs +34 -5
- package/dist/cjs/fields/RadioGroup/index.native.js +49 -7
- package/dist/cjs/fields/RadioGroup/index.native.js.map +1 -1
- package/dist/cjs/fields/SearchInput/index.cjs +8 -9
- package/dist/cjs/fields/SearchInput/index.native.js +8 -9
- package/dist/cjs/fields/SearchInput/index.native.js.map +1 -1
- package/dist/cjs/fields/Select/index.cjs +42 -11
- package/dist/cjs/fields/Select/index.native.js +52 -20
- package/dist/cjs/fields/Select/index.native.js.map +1 -1
- package/dist/cjs/fields/Stepper/index.cjs +144 -31
- package/dist/cjs/fields/Stepper/index.native.js +151 -31
- package/dist/cjs/fields/Stepper/index.native.js.map +1 -1
- package/dist/cjs/fields/ToggleGroup/index.cjs +35 -2
- package/dist/cjs/fields/ToggleGroup/index.native.js +42 -2
- package/dist/cjs/fields/ToggleGroup/index.native.js.map +1 -1
- package/dist/cjs/index.cjs +2 -0
- package/dist/cjs/index.native.js +2 -0
- package/dist/cjs/index.native.js.map +1 -1
- package/dist/cjs/shared/focusManagement.cjs +24 -11
- package/dist/cjs/shared/focusManagement.native.js +24 -11
- package/dist/cjs/shared/focusManagement.native.js.map +1 -1
- package/dist/cjs/shared/nativeA11y.cjs +57 -0
- package/dist/cjs/shared/nativeA11y.native.js +61 -0
- package/dist/cjs/shared/nativeA11y.native.js.map +1 -0
- package/dist/esm/Button/index.mjs +1 -4
- package/dist/esm/Button/index.mjs.map +1 -1
- package/dist/esm/Button/index.native.js +5 -6
- package/dist/esm/Button/index.native.js.map +1 -1
- package/dist/esm/Chip/index.mjs +1 -1
- package/dist/esm/Chip/index.mjs.map +1 -1
- package/dist/esm/Chip/index.native.js +1 -1
- package/dist/esm/Chip/index.native.js.map +1 -1
- package/dist/esm/FloatingPanel/index.mjs +58 -15
- package/dist/esm/FloatingPanel/index.mjs.map +1 -1
- package/dist/esm/FloatingPanel/index.native.js +15 -8
- package/dist/esm/FloatingPanel/index.native.js.map +1 -1
- package/dist/esm/FloatingPanel/nestedAction.mjs +11 -0
- package/dist/esm/FloatingPanel/nestedAction.mjs.map +1 -0
- package/dist/esm/FloatingPanel/nestedAction.native.js +13 -0
- package/dist/esm/FloatingPanel/nestedAction.native.js.map +1 -0
- package/dist/esm/InputParts/index.mjs +20 -9
- package/dist/esm/InputParts/index.mjs.map +1 -1
- package/dist/esm/InputParts/index.native.js +21 -12
- package/dist/esm/InputParts/index.native.js.map +1 -1
- package/dist/esm/Wheel/index.mjs +3 -3
- package/dist/esm/Wheel/index.mjs.map +1 -1
- package/dist/esm/Wheel/index.native.js +3 -3
- package/dist/esm/Wheel/index.native.js.map +1 -1
- package/dist/esm/fieldLayout.mjs +3 -3
- package/dist/esm/fieldLayout.mjs.map +1 -1
- package/dist/esm/fieldLayout.native.js +3 -3
- package/dist/esm/fieldLayout.native.js.map +1 -1
- package/dist/esm/fields/Checkbox/CheckboxFieldFrame.mjs +8 -1
- package/dist/esm/fields/Checkbox/CheckboxFieldFrame.mjs.map +1 -1
- package/dist/esm/fields/Checkbox/CheckboxFieldFrame.native.js +10 -1
- package/dist/esm/fields/Checkbox/CheckboxFieldFrame.native.js.map +1 -1
- package/dist/esm/fields/Combobox/index.mjs +36 -29
- package/dist/esm/fields/Combobox/index.mjs.map +1 -1
- package/dist/esm/fields/Combobox/index.native.js +38 -38
- package/dist/esm/fields/Combobox/index.native.js.map +1 -1
- package/dist/esm/fields/DatePicker/Calendar.mjs +6 -1
- package/dist/esm/fields/DatePicker/Calendar.mjs.map +1 -1
- package/dist/esm/fields/DatePicker/Calendar.native.js +6 -1
- package/dist/esm/fields/DatePicker/Calendar.native.js.map +1 -1
- package/dist/esm/fields/DatePicker/DateRangePicker.mjs +2 -2
- package/dist/esm/fields/DatePicker/DateRangePicker.mjs.map +1 -1
- package/dist/esm/fields/DatePicker/DateRangePicker.native.js +2 -2
- package/dist/esm/fields/DatePicker/DateRangePicker.native.js.map +1 -1
- package/dist/esm/fields/DatePicker/DatetimePicker.catalog.mjs +15 -12
- package/dist/esm/fields/DatePicker/DatetimePicker.catalog.mjs.map +1 -1
- package/dist/esm/fields/DatePicker/DatetimePicker.catalog.native.js +16 -12
- package/dist/esm/fields/DatePicker/DatetimePicker.catalog.native.js.map +1 -1
- package/dist/esm/fields/DatePicker/DatetimePicker.native.js +39 -14
- package/dist/esm/fields/DatePicker/DatetimePicker.native.js.map +1 -1
- package/dist/esm/fields/DatePicker/MonthPicker.mjs +10 -3
- package/dist/esm/fields/DatePicker/MonthPicker.mjs.map +1 -1
- package/dist/esm/fields/DatePicker/MonthPicker.native.js +10 -3
- package/dist/esm/fields/DatePicker/MonthPicker.native.js.map +1 -1
- package/dist/esm/fields/DatePicker/MultiDatePicker.mjs +2 -2
- package/dist/esm/fields/DatePicker/MultiDatePicker.mjs.map +1 -1
- package/dist/esm/fields/DatePicker/MultiDatePicker.native.js +2 -2
- package/dist/esm/fields/DatePicker/MultiDatePicker.native.js.map +1 -1
- package/dist/esm/fields/DatePicker/index.native.js +3 -0
- package/dist/esm/fields/DatePicker/index.native.js.map +1 -1
- package/dist/esm/fields/DatePicker/parts.mjs +28 -16
- package/dist/esm/fields/DatePicker/parts.mjs.map +1 -1
- package/dist/esm/fields/DatePicker/parts.native.js +30 -16
- package/dist/esm/fields/DatePicker/parts.native.js.map +1 -1
- package/dist/esm/fields/FileUpload/index.mjs +6 -0
- package/dist/esm/fields/FileUpload/index.mjs.map +1 -1
- package/dist/esm/fields/FileUpload/index.native.js +6 -0
- package/dist/esm/fields/FileUpload/index.native.js.map +1 -1
- package/dist/esm/fields/Geolocation/GeolocationMap.mjs.map +1 -1
- package/dist/esm/fields/Geolocation/GeolocationMap.native.js.map +1 -1
- package/dist/esm/fields/Geolocation/geocoder.mjs +33 -0
- package/dist/esm/fields/Geolocation/geocoder.mjs.map +1 -0
- package/dist/esm/fields/Geolocation/geocoder.native.js +35 -0
- package/dist/esm/fields/Geolocation/geocoder.native.js.map +1 -0
- package/dist/esm/fields/Geolocation/index.mjs +792 -149
- package/dist/esm/fields/Geolocation/index.mjs.map +1 -1
- package/dist/esm/fields/Geolocation/index.native.js +847 -164
- package/dist/esm/fields/Geolocation/index.native.js.map +1 -1
- package/dist/esm/fields/Geolocation/nativeSearchFocus.mjs +19 -0
- package/dist/esm/fields/Geolocation/nativeSearchFocus.mjs.map +1 -0
- package/dist/esm/fields/Geolocation/nativeSearchFocus.native.js +19 -0
- package/dist/esm/fields/Geolocation/nativeSearchFocus.native.js.map +1 -0
- package/dist/esm/fields/Icon/drawnSet.mjs +16 -3
- package/dist/esm/fields/Icon/drawnSet.mjs.map +1 -1
- package/dist/esm/fields/Icon/drawnSet.native.js +20 -3
- package/dist/esm/fields/Icon/drawnSet.native.js.map +1 -1
- package/dist/esm/fields/Icon/index.mjs +93 -18
- package/dist/esm/fields/Icon/index.mjs.map +1 -1
- package/dist/esm/fields/Icon/index.native.js +106 -22
- package/dist/esm/fields/Icon/index.native.js.map +1 -1
- package/dist/esm/fields/Input/index.mjs +5 -12
- package/dist/esm/fields/Input/index.mjs.map +1 -1
- package/dist/esm/fields/Input/index.native.js +5 -13
- package/dist/esm/fields/Input/index.native.js.map +1 -1
- package/dist/esm/fields/PhoneInput/index.mjs +2 -2
- package/dist/esm/fields/PhoneInput/index.mjs.map +1 -1
- package/dist/esm/fields/PhoneInput/index.native.js +2 -2
- package/dist/esm/fields/PhoneInput/index.native.js.map +1 -1
- package/dist/esm/fields/RadioGroup/index.mjs +34 -5
- package/dist/esm/fields/RadioGroup/index.mjs.map +1 -1
- package/dist/esm/fields/RadioGroup/index.native.js +49 -7
- package/dist/esm/fields/RadioGroup/index.native.js.map +1 -1
- package/dist/esm/fields/SearchInput/index.mjs +9 -10
- package/dist/esm/fields/SearchInput/index.mjs.map +1 -1
- package/dist/esm/fields/SearchInput/index.native.js +9 -10
- package/dist/esm/fields/SearchInput/index.native.js.map +1 -1
- package/dist/esm/fields/Select/index.mjs +42 -11
- package/dist/esm/fields/Select/index.mjs.map +1 -1
- package/dist/esm/fields/Select/index.native.js +52 -20
- package/dist/esm/fields/Select/index.native.js.map +1 -1
- package/dist/esm/fields/Stepper/index.mjs +145 -32
- package/dist/esm/fields/Stepper/index.mjs.map +1 -1
- package/dist/esm/fields/Stepper/index.native.js +152 -32
- package/dist/esm/fields/Stepper/index.native.js.map +1 -1
- package/dist/esm/fields/ToggleGroup/index.mjs +35 -2
- package/dist/esm/fields/ToggleGroup/index.mjs.map +1 -1
- package/dist/esm/fields/ToggleGroup/index.native.js +42 -2
- package/dist/esm/fields/ToggleGroup/index.native.js.map +1 -1
- package/dist/esm/index.mjs +2 -1
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/index.native.js +2 -1
- package/dist/esm/index.native.js.map +1 -1
- package/dist/esm/shared/focusManagement.mjs +24 -11
- package/dist/esm/shared/focusManagement.mjs.map +1 -1
- package/dist/esm/shared/focusManagement.native.js +24 -11
- package/dist/esm/shared/focusManagement.native.js.map +1 -1
- package/dist/esm/shared/nativeA11y.mjs +31 -0
- package/dist/esm/shared/nativeA11y.mjs.map +1 -0
- package/dist/esm/shared/nativeA11y.native.js +32 -0
- package/dist/esm/shared/nativeA11y.native.js.map +1 -0
- package/dist/jsx/Button/index.mjs +1 -4
- package/dist/jsx/Button/index.mjs.map +1 -1
- package/dist/jsx/Button/index.native.js +5 -6
- package/dist/jsx/Button/index.native.js.map +1 -1
- package/dist/jsx/Chip/index.mjs +1 -1
- package/dist/jsx/Chip/index.mjs.map +1 -1
- package/dist/jsx/Chip/index.native.js +1 -1
- package/dist/jsx/Chip/index.native.js.map +1 -1
- package/dist/jsx/FloatingPanel/index.mjs +58 -15
- package/dist/jsx/FloatingPanel/index.mjs.map +1 -1
- package/dist/jsx/FloatingPanel/index.native.js +16 -7
- package/dist/jsx/FloatingPanel/index.native.js.map +1 -1
- package/dist/jsx/FloatingPanel/nestedAction.mjs +11 -0
- package/dist/jsx/FloatingPanel/nestedAction.mjs.map +1 -0
- package/dist/jsx/FloatingPanel/nestedAction.native.js +42 -0
- package/dist/jsx/FloatingPanel/nestedAction.native.js.map +1 -0
- package/dist/jsx/InputParts/index.mjs +20 -9
- package/dist/jsx/InputParts/index.mjs.map +1 -1
- package/dist/jsx/InputParts/index.native.js +20 -11
- package/dist/jsx/InputParts/index.native.js.map +1 -1
- package/dist/jsx/Wheel/index.mjs +3 -3
- package/dist/jsx/Wheel/index.mjs.map +1 -1
- package/dist/jsx/Wheel/index.native.js +3 -3
- package/dist/jsx/Wheel/index.native.js.map +1 -1
- package/dist/jsx/fieldLayout.mjs +3 -3
- package/dist/jsx/fieldLayout.mjs.map +1 -1
- package/dist/jsx/fieldLayout.native.js +3 -3
- package/dist/jsx/fieldLayout.native.js.map +1 -1
- package/dist/jsx/fields/Checkbox/CheckboxFieldFrame.mjs +8 -1
- package/dist/jsx/fields/Checkbox/CheckboxFieldFrame.mjs.map +1 -1
- package/dist/jsx/fields/Checkbox/CheckboxFieldFrame.native.js +10 -1
- package/dist/jsx/fields/Checkbox/CheckboxFieldFrame.native.js.map +1 -1
- package/dist/jsx/fields/Combobox/index.mjs +36 -29
- package/dist/jsx/fields/Combobox/index.mjs.map +1 -1
- package/dist/jsx/fields/Combobox/index.native.js +38 -38
- package/dist/jsx/fields/Combobox/index.native.js.map +1 -1
- package/dist/jsx/fields/DatePicker/Calendar.mjs +6 -1
- package/dist/jsx/fields/DatePicker/Calendar.mjs.map +1 -1
- package/dist/jsx/fields/DatePicker/Calendar.native.js +6 -1
- package/dist/jsx/fields/DatePicker/Calendar.native.js.map +1 -1
- package/dist/jsx/fields/DatePicker/DateRangePicker.mjs +2 -2
- package/dist/jsx/fields/DatePicker/DateRangePicker.mjs.map +1 -1
- package/dist/jsx/fields/DatePicker/DateRangePicker.native.js +2 -2
- package/dist/jsx/fields/DatePicker/DateRangePicker.native.js.map +1 -1
- package/dist/jsx/fields/DatePicker/DatetimePicker.catalog.mjs +15 -12
- package/dist/jsx/fields/DatePicker/DatetimePicker.catalog.mjs.map +1 -1
- package/dist/jsx/fields/DatePicker/DatetimePicker.catalog.native.js +16 -12
- package/dist/jsx/fields/DatePicker/DatetimePicker.catalog.native.js.map +1 -1
- package/dist/jsx/fields/DatePicker/DatetimePicker.native.js +39 -14
- package/dist/jsx/fields/DatePicker/DatetimePicker.native.js.map +1 -1
- package/dist/jsx/fields/DatePicker/MonthPicker.mjs +10 -3
- package/dist/jsx/fields/DatePicker/MonthPicker.mjs.map +1 -1
- package/dist/jsx/fields/DatePicker/MonthPicker.native.js +10 -3
- package/dist/jsx/fields/DatePicker/MonthPicker.native.js.map +1 -1
- package/dist/jsx/fields/DatePicker/MultiDatePicker.mjs +2 -2
- package/dist/jsx/fields/DatePicker/MultiDatePicker.mjs.map +1 -1
- package/dist/jsx/fields/DatePicker/MultiDatePicker.native.js +2 -2
- package/dist/jsx/fields/DatePicker/MultiDatePicker.native.js.map +1 -1
- package/dist/jsx/fields/DatePicker/index.native.js +3 -0
- package/dist/jsx/fields/DatePicker/index.native.js.map +1 -1
- package/dist/jsx/fields/DatePicker/parts.mjs +28 -16
- package/dist/jsx/fields/DatePicker/parts.mjs.map +1 -1
- package/dist/jsx/fields/DatePicker/parts.native.js +28 -14
- package/dist/jsx/fields/DatePicker/parts.native.js.map +1 -1
- package/dist/jsx/fields/FileUpload/index.mjs +6 -0
- package/dist/jsx/fields/FileUpload/index.mjs.map +1 -1
- package/dist/jsx/fields/FileUpload/index.native.js +6 -0
- package/dist/jsx/fields/FileUpload/index.native.js.map +1 -1
- package/dist/jsx/fields/Geolocation/GeolocationMap.mjs.map +1 -1
- package/dist/jsx/fields/Geolocation/GeolocationMap.native.js.map +1 -1
- package/dist/jsx/fields/Geolocation/geocoder.mjs +33 -0
- package/dist/jsx/fields/Geolocation/geocoder.mjs.map +1 -0
- package/dist/jsx/fields/Geolocation/geocoder.native.js +68 -0
- package/dist/jsx/fields/Geolocation/geocoder.native.js.map +1 -0
- package/dist/jsx/fields/Geolocation/index.mjs +792 -149
- package/dist/jsx/fields/Geolocation/index.mjs.map +1 -1
- package/dist/jsx/fields/Geolocation/index.native.js +856 -167
- package/dist/jsx/fields/Geolocation/index.native.js.map +1 -1
- package/dist/jsx/fields/Geolocation/nativeSearchFocus.mjs +19 -0
- package/dist/jsx/fields/Geolocation/nativeSearchFocus.mjs.map +1 -0
- package/dist/jsx/fields/Geolocation/nativeSearchFocus.native.js +47 -0
- package/dist/jsx/fields/Geolocation/nativeSearchFocus.native.js.map +1 -0
- package/dist/jsx/fields/Icon/drawnSet.mjs +16 -3
- package/dist/jsx/fields/Icon/drawnSet.mjs.map +1 -1
- package/dist/jsx/fields/Icon/drawnSet.native.js +20 -3
- package/dist/jsx/fields/Icon/drawnSet.native.js.map +1 -1
- package/dist/jsx/fields/Icon/index.mjs +93 -18
- package/dist/jsx/fields/Icon/index.mjs.map +1 -1
- package/dist/jsx/fields/Icon/index.native.js +115 -31
- package/dist/jsx/fields/Icon/index.native.js.map +1 -1
- package/dist/jsx/fields/Input/index.mjs +5 -12
- package/dist/jsx/fields/Input/index.mjs.map +1 -1
- package/dist/jsx/fields/Input/index.native.js +5 -13
- package/dist/jsx/fields/Input/index.native.js.map +1 -1
- package/dist/jsx/fields/PhoneInput/index.mjs +2 -2
- package/dist/jsx/fields/PhoneInput/index.mjs.map +1 -1
- package/dist/jsx/fields/PhoneInput/index.native.js +2 -2
- package/dist/jsx/fields/PhoneInput/index.native.js.map +1 -1
- package/dist/jsx/fields/RadioGroup/index.mjs +34 -5
- package/dist/jsx/fields/RadioGroup/index.mjs.map +1 -1
- package/dist/jsx/fields/RadioGroup/index.native.js +49 -7
- package/dist/jsx/fields/RadioGroup/index.native.js.map +1 -1
- package/dist/jsx/fields/SearchInput/index.mjs +9 -10
- package/dist/jsx/fields/SearchInput/index.mjs.map +1 -1
- package/dist/jsx/fields/SearchInput/index.native.js +8 -9
- package/dist/jsx/fields/SearchInput/index.native.js.map +1 -1
- package/dist/jsx/fields/Select/index.mjs +42 -11
- package/dist/jsx/fields/Select/index.mjs.map +1 -1
- package/dist/jsx/fields/Select/index.native.js +52 -20
- package/dist/jsx/fields/Select/index.native.js.map +1 -1
- package/dist/jsx/fields/Stepper/index.mjs +145 -32
- package/dist/jsx/fields/Stepper/index.mjs.map +1 -1
- package/dist/jsx/fields/Stepper/index.native.js +151 -31
- package/dist/jsx/fields/Stepper/index.native.js.map +1 -1
- package/dist/jsx/fields/ToggleGroup/index.mjs +35 -2
- package/dist/jsx/fields/ToggleGroup/index.mjs.map +1 -1
- package/dist/jsx/fields/ToggleGroup/index.native.js +42 -2
- package/dist/jsx/fields/ToggleGroup/index.native.js.map +1 -1
- package/dist/jsx/index.js +2 -1
- package/dist/jsx/index.js.map +1 -1
- package/dist/jsx/index.mjs +2 -1
- package/dist/jsx/index.mjs.map +1 -1
- package/dist/jsx/index.native.js +2 -0
- package/dist/jsx/index.native.js.map +1 -1
- package/dist/jsx/shared/focusManagement.mjs +24 -11
- package/dist/jsx/shared/focusManagement.mjs.map +1 -1
- package/dist/jsx/shared/focusManagement.native.js +24 -11
- package/dist/jsx/shared/focusManagement.native.js.map +1 -1
- package/dist/jsx/shared/nativeA11y.mjs +31 -0
- package/dist/jsx/shared/nativeA11y.mjs.map +1 -0
- package/dist/jsx/shared/nativeA11y.native.js +61 -0
- package/dist/jsx/shared/nativeA11y.native.js.map +1 -0
- package/package.json +9 -7
- package/src/Button/Button.fontWeight.spec.tsx +75 -0
- package/src/Button/Button.labelInk.spec.tsx +21 -18
- package/src/Button/Button.outlinedChannel.spec.tsx +46 -0
- package/src/Button/Button.sizeLadder.spec.tsx +4 -3
- package/src/Button/Button.stories.tsx +20 -0
- package/src/Button/index.tsx +30 -7
- package/src/Chip/Chip.spec.tsx +3 -1
- package/src/Chip/index.tsx +1 -5
- package/src/FieldGroup.stories.tsx +3 -7
- package/src/FieldLayout.stories.tsx +75 -12
- package/src/Fieldset.stories.tsx +3 -7
- package/src/FloatingPanel/FloatingPanel.native.spec.tsx +54 -0
- package/src/FloatingPanel/FloatingPanel.spec.tsx +109 -0
- package/src/FloatingPanel/index.native.tsx +24 -7
- package/src/FloatingPanel/index.tsx +80 -13
- package/src/FloatingPanel/nestedAction.spec.ts +36 -0
- package/src/FloatingPanel/nestedAction.ts +15 -0
- package/src/FloatingPanel/panelWidthContract.spec.ts +12 -15
- package/src/FormGrid.stories.tsx +5 -11
- package/src/FormsIntegration.stories.tsx +2 -6
- package/src/InputParts/InputButton.spec.tsx +16 -0
- package/src/InputParts/index.tsx +34 -7
- package/src/InputParts/layoutGap.spec.tsx +43 -0
- package/src/Wheel/Wheel.spec.tsx +16 -0
- package/src/Wheel/index.tsx +3 -3
- package/src/fieldFamilyStories.radius.spec.tsx +118 -0
- package/src/fieldLayout.spec.tsx +42 -1
- package/src/fieldLayout.tsx +9 -9
- package/src/fields/Barcode/Barcode.spec.tsx +2 -0
- package/src/fields/Checkbox/CheckboxFieldFrame.spec.tsx +18 -0
- package/src/fields/Checkbox/CheckboxFieldFrame.tsx +6 -1
- package/src/fields/Combobox/Combobox.spec.tsx +48 -1
- package/src/fields/Combobox/index.tsx +53 -39
- package/src/fields/DatePicker/Calendar.tsx +4 -1
- package/src/fields/DatePicker/DatePicker.stories.tsx +22 -1
- package/src/fields/DatePicker/DateRangePicker.tsx +4 -2
- package/src/fields/DatePicker/DatetimePicker.catalog.tsx +21 -19
- package/src/fields/DatePicker/DatetimePicker.native.spec.tsx +144 -0
- package/src/fields/DatePicker/DatetimePicker.native.tsx +57 -32
- package/src/fields/DatePicker/DatetimePicker.stories.tsx +22 -0
- package/src/fields/DatePicker/MonthPicker.tsx +8 -3
- package/src/fields/DatePicker/MultiDatePicker.tsx +4 -2
- package/src/fields/DatePicker/index.native.tsx +1 -0
- package/src/fields/DatePicker/parts.spec.tsx +56 -0
- package/src/fields/DatePicker/parts.tsx +33 -12
- package/src/fields/FileUpload/FileUpload.spec.tsx +29 -0
- package/src/fields/FileUpload/index.tsx +6 -0
- package/src/fields/Geolocation/Geolocation.spec.tsx +287 -35
- package/src/fields/Geolocation/Geolocation.stories.tsx +141 -7
- package/src/fields/Geolocation/GeolocationMap.native.tsx +3 -0
- package/src/fields/Geolocation/GeolocationMap.tsx +4 -0
- package/src/fields/Geolocation/geocoder.spec.ts +59 -0
- package/src/fields/Geolocation/geocoder.ts +84 -0
- package/src/fields/Geolocation/geocoderSeam.spec.tsx +203 -0
- package/src/fields/Geolocation/index.tsx +912 -180
- package/src/fields/Geolocation/nativeSearchFocus.spec.ts +34 -0
- package/src/fields/Geolocation/nativeSearchFocus.ts +19 -0
- package/src/fields/Geolocation/salvage.spec.tsx +398 -0
- package/src/fields/Geolocation/storyProviders.spec.tsx +44 -0
- package/src/fields/Icon/Icon.spec.tsx +215 -1
- package/src/fields/Icon/Icon.stories.tsx +9 -1
- package/src/fields/Icon/drawnSet.tsx +44 -4
- package/src/fields/Icon/index.tsx +118 -18
- package/src/fields/Input/Input.copy.spec.tsx +1 -0
- package/src/fields/Input/Input.spec.tsx +50 -0
- package/src/fields/Input/Input.stories.tsx +1 -1
- package/src/fields/Input/index.tsx +7 -13
- package/src/fields/PhoneInput/index.tsx +2 -2
- package/src/fields/RadioGroup/RadioGroup.native.spec.tsx +67 -0
- package/src/fields/RadioGroup/index.tsx +82 -8
- package/src/fields/SearchInput/SearchInput.spec.tsx +14 -0
- package/src/fields/SearchInput/index.tsx +5 -6
- package/src/fields/Select/Select.spec.tsx +54 -0
- package/src/fields/Select/index.tsx +52 -17
- package/src/fields/Stepper/Stepper.spec.tsx +34 -1
- package/src/fields/Stepper/index.tsx +193 -33
- package/src/fields/ToggleGroup/ToggleGroup.native.spec.tsx +73 -0
- package/src/fields/ToggleGroup/ToggleGroup.spec.tsx +49 -0
- package/src/fields/ToggleGroup/index.tsx +76 -1
- package/src/index.ts +9 -0
- package/src/shared/focusManagement.spec.tsx +68 -0
- package/src/shared/focusManagement.ts +20 -10
- package/src/shared/nativeA11y.spec.ts +64 -0
- package/src/shared/nativeA11y.ts +80 -0
- package/types/Button/index.d.ts +22 -3
- package/types/Button/index.d.ts.map +1 -1
- package/types/Chip/index.d.ts.map +1 -1
- package/types/FloatingPanel/index.d.ts +11 -1
- package/types/FloatingPanel/index.d.ts.map +1 -1
- package/types/FloatingPanel/index.native.d.ts +2 -1
- package/types/FloatingPanel/index.native.d.ts.map +1 -1
- package/types/FloatingPanel/nestedAction.d.ts +13 -0
- package/types/FloatingPanel/nestedAction.d.ts.map +1 -0
- package/types/InputParts/index.d.ts.map +1 -1
- package/types/fields/Checkbox/CheckboxFieldFrame.d.ts +46 -2
- package/types/fields/Checkbox/CheckboxFieldFrame.d.ts.map +1 -1
- package/types/fields/Checkbox/index.d.ts +3 -3
- package/types/fields/Combobox/index.d.ts +3 -1
- package/types/fields/Combobox/index.d.ts.map +1 -1
- package/types/fields/DatePicker/Calendar.d.ts.map +1 -1
- package/types/fields/DatePicker/DateRangePicker.d.ts.map +1 -1
- package/types/fields/DatePicker/DatetimePicker.catalog.d.ts.map +1 -1
- package/types/fields/DatePicker/DatetimePicker.native.d.ts.map +1 -1
- package/types/fields/DatePicker/MonthPicker.d.ts.map +1 -1
- package/types/fields/DatePicker/MultiDatePicker.d.ts.map +1 -1
- package/types/fields/DatePicker/index.native.d.ts.map +1 -1
- package/types/fields/DatePicker/parts.d.ts.map +1 -1
- package/types/fields/FileUpload/index.d.ts.map +1 -1
- package/types/fields/Geolocation/GeolocationMap.d.ts +4 -0
- package/types/fields/Geolocation/GeolocationMap.d.ts.map +1 -1
- package/types/fields/Geolocation/GeolocationMap.native.d.ts +3 -0
- package/types/fields/Geolocation/GeolocationMap.native.d.ts.map +1 -1
- package/types/fields/Geolocation/geocoder.d.ts +53 -0
- package/types/fields/Geolocation/geocoder.d.ts.map +1 -0
- package/types/fields/Geolocation/index.d.ts +18 -2
- package/types/fields/Geolocation/index.d.ts.map +1 -1
- package/types/fields/Geolocation/nativeSearchFocus.d.ts +8 -0
- package/types/fields/Geolocation/nativeSearchFocus.d.ts.map +1 -0
- package/types/fields/Icon/drawnSet.d.ts.map +1 -1
- package/types/fields/Icon/index.d.ts.map +1 -1
- package/types/fields/Input/index.d.ts.map +1 -1
- package/types/fields/PhoneInput/index.d.ts.map +1 -1
- package/types/fields/RadioGroup/index.d.ts +30 -3
- package/types/fields/RadioGroup/index.d.ts.map +1 -1
- package/types/fields/SearchInput/index.d.ts.map +1 -1
- package/types/fields/Select/index.d.ts.map +1 -1
- package/types/fields/Stepper/index.d.ts.map +1 -1
- package/types/fields/ToggleGroup/index.d.ts +31 -2
- package/types/fields/ToggleGroup/index.d.ts.map +1 -1
- package/types/index.d.ts +2 -0
- package/types/index.d.ts.map +1 -1
- package/types/shared/focusManagement.d.ts +2 -2
- package/types/shared/focusManagement.d.ts.map +1 -1
- package/types/shared/nativeA11y.d.ts +61 -0
- package/types/shared/nativeA11y.d.ts.map +1 -0
- package/src/router.d.ts +0 -16
|
@@ -1,23 +1,33 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
ensureFocusVisibleRing,
|
|
3
|
+
pressTargetHitSlop,
|
|
4
|
+
useResolvedKnobs,
|
|
5
|
+
useGlyphColor,
|
|
6
|
+
type KnobProps,
|
|
7
|
+
} from "@multiplatform.one/theme";
|
|
8
|
+
import { MagnifyingGlassIcon, MapPinIcon, NavigationArrowIcon, XIcon } from "@phosphor-icons/react";
|
|
3
9
|
import type { KeyboardEvent, MutableRefObject, ReactNode } from "react";
|
|
4
|
-
import { useCallback, useEffect, useRef, useState } from "react";
|
|
10
|
+
import { useCallback, useEffect, useId, useRef, useState } from "react";
|
|
5
11
|
import type { LabelProps, SizeTokens } from "tamagui";
|
|
6
|
-
import { Spinner, Text, View, XStack, YStack, isWeb } from "tamagui";
|
|
12
|
+
import { ScrollView, Spinner, Text, View, XStack, YStack, isWeb } from "tamagui";
|
|
7
13
|
import { AdaptivePopup } from "../../AdaptivePopup";
|
|
8
14
|
import { Button } from "../../Button";
|
|
9
15
|
import { Field, FieldLayout } from "../../fieldLayout";
|
|
10
|
-
import { FloatingPanel } from "../../FloatingPanel";
|
|
11
|
-
import { GeolocationMap } from "./GeolocationMap";
|
|
12
|
-
import { detectGeoShape, formatGeoValue, type GeoValue, parseGeoValue } from "./geoValue";
|
|
16
|
+
import { FloatingPanel, useViewportGtSm } from "../../FloatingPanel";
|
|
13
17
|
import { Input as InputParts } from "../../InputParts";
|
|
14
18
|
import { Skeleton } from "../../Skeleton";
|
|
19
|
+
import { GeolocationMap } from "./GeolocationMap";
|
|
20
|
+
import { createNativeSearchFocus } from "./nativeSearchFocus";
|
|
21
|
+
import { detectGeoShape, formatGeoValue, parseGeoValue } from "./geoValue";
|
|
15
22
|
import {
|
|
16
23
|
queryPermissionState,
|
|
17
24
|
type CapabilityPreflightResult,
|
|
18
25
|
} from "../../shared/capabilityPreflight";
|
|
19
26
|
import { formCommonColors, formInputColors } from "../../shared/colorRamps";
|
|
20
27
|
import { t } from "../../shared/t";
|
|
28
|
+
import { pressSlopProps } from "../../shared/pressSlopProps";
|
|
29
|
+
import { useIsInTableCell } from "../../shared/tableCellContext";
|
|
30
|
+
import { zIndex } from "../../shared/zIndex";
|
|
21
31
|
import {
|
|
22
32
|
clampRadiusForLargeComponent,
|
|
23
33
|
getFieldError,
|
|
@@ -27,8 +37,44 @@ import {
|
|
|
27
37
|
useResolvedValidators,
|
|
28
38
|
} from "../../shared/utils";
|
|
29
39
|
import type { AnyFormApi } from "../../types";
|
|
40
|
+
import type { GeocodeFn, GeocodeResult, Geocoder, ReverseGeocodeFn } from "./geocoder";
|
|
41
|
+
import {
|
|
42
|
+
clampLat,
|
|
43
|
+
clampLng,
|
|
44
|
+
formatCoordPair,
|
|
45
|
+
formatPlace,
|
|
46
|
+
parseLatLngPair,
|
|
47
|
+
resolveGeocoder,
|
|
48
|
+
} from "./geocoder";
|
|
49
|
+
|
|
50
|
+
export type {
|
|
51
|
+
GeocodeFn,
|
|
52
|
+
GeocodeQuery,
|
|
53
|
+
GeocodeResult,
|
|
54
|
+
Geocoder,
|
|
55
|
+
ReverseGeocodeFn,
|
|
56
|
+
ReverseGeocodeQuery,
|
|
57
|
+
} from "./geocoder";
|
|
58
|
+
export {
|
|
59
|
+
clampLat,
|
|
60
|
+
clampLng,
|
|
61
|
+
formatCoordPair,
|
|
62
|
+
formatPlace,
|
|
63
|
+
parseLatLngPair,
|
|
64
|
+
resolveGeocoder,
|
|
65
|
+
} from "./geocoder";
|
|
30
66
|
|
|
31
67
|
type LocatePhase = "idle" | "locating" | "error";
|
|
68
|
+
type SearchStatus = "idle" | "searching" | "results" | "empty" | "error";
|
|
69
|
+
type ResolvedAddress = { key: string; label: string };
|
|
70
|
+
|
|
71
|
+
function coordinateKey({ lat, lng }: { lat: number; lng: number }): string {
|
|
72
|
+
return `${lat},${lng}`;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const SEARCH_DEBOUNCE_MS = 300;
|
|
76
|
+
const COORD_LINE_HEIGHT = 28;
|
|
77
|
+
const RESULTS_PANEL_SHADOW = "0px 8px 28px rgba(0,0,0,0.12), 0px 2px 6px rgba(0,0,0,0.04)";
|
|
32
78
|
|
|
33
79
|
/**
|
|
34
80
|
* Geolocation capability pre-flight (LC-19 CAPABILITY-PREFLIGHT). Runs BEFORE
|
|
@@ -75,17 +121,6 @@ function mapGeolocationError(
|
|
|
75
121
|
return err.message || t("Failed to get location");
|
|
76
122
|
}
|
|
77
123
|
|
|
78
|
-
function clampLat(val: number): number {
|
|
79
|
-
return Math.max(-90, Math.min(90, val));
|
|
80
|
-
}
|
|
81
|
-
function clampLng(val: number): number {
|
|
82
|
-
return Math.max(-180, Math.min(180, val));
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function formatCoordChip(lat: number, lng: number): string {
|
|
86
|
-
return `${lat.toFixed(4)}, ${lng.toFixed(4)}`;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
124
|
/**
|
|
90
125
|
* Parse a coordinate draft. Returns undefined while the user is mid-keystroke
|
|
91
126
|
* (empty, lone minus, trailing decimal) so we don't clobber the bound value.
|
|
@@ -144,7 +179,6 @@ function CoordinatesPanel({
|
|
|
144
179
|
[onCommit],
|
|
145
180
|
);
|
|
146
181
|
|
|
147
|
-
/** Dismiss/blur flush — more permissive than live parse (accepts trailing "."). */
|
|
148
182
|
const flushDraft = useCallback(() => {
|
|
149
183
|
const rawLat = Number.parseFloat(latTextRef.current);
|
|
150
184
|
const rawLng = Number.parseFloat(lngTextRef.current);
|
|
@@ -269,8 +303,159 @@ export interface GeolocationMapProps {
|
|
|
269
303
|
knobProps: KnobProps;
|
|
270
304
|
}
|
|
271
305
|
|
|
272
|
-
/** Overlay buttons sit inside the map
|
|
273
|
-
const overlayFocusRing = ensureFocusVisibleRing({ outlineOffset:
|
|
306
|
+
/** Overlay buttons sit inside the map — 2px ring at offset 0 (media-13). */
|
|
307
|
+
const overlayFocusRing = ensureFocusVisibleRing({ outlineOffset: 0 });
|
|
308
|
+
|
|
309
|
+
function ResultRow({
|
|
310
|
+
testID,
|
|
311
|
+
label,
|
|
312
|
+
description,
|
|
313
|
+
onPress,
|
|
314
|
+
disabled,
|
|
315
|
+
}: {
|
|
316
|
+
testID?: string;
|
|
317
|
+
label: string;
|
|
318
|
+
description?: string;
|
|
319
|
+
onPress: () => void;
|
|
320
|
+
disabled?: boolean;
|
|
321
|
+
}) {
|
|
322
|
+
return (
|
|
323
|
+
<YStack
|
|
324
|
+
testID={testID}
|
|
325
|
+
role="option"
|
|
326
|
+
aria-selected={false}
|
|
327
|
+
aria-disabled={disabled || undefined}
|
|
328
|
+
{...(isWeb
|
|
329
|
+
? {
|
|
330
|
+
tabIndex: disabled ? -1 : 0,
|
|
331
|
+
onKeyDown: (event: KeyboardEvent<HTMLElement>) => {
|
|
332
|
+
if (disabled) return;
|
|
333
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
334
|
+
event.preventDefault();
|
|
335
|
+
onPress();
|
|
336
|
+
} else if (event.key === "ArrowDown" || event.key === "ArrowUp") {
|
|
337
|
+
event.preventDefault();
|
|
338
|
+
const rows = [
|
|
339
|
+
...(event.currentTarget.parentElement?.querySelectorAll<HTMLElement>(
|
|
340
|
+
"[role='option']:not([aria-disabled='true'])",
|
|
341
|
+
) ?? []),
|
|
342
|
+
];
|
|
343
|
+
const index = rows.indexOf(event.currentTarget);
|
|
344
|
+
rows[
|
|
345
|
+
(index + (event.key === "ArrowDown" ? 1 : -1) + rows.length) % rows.length
|
|
346
|
+
]?.focus();
|
|
347
|
+
}
|
|
348
|
+
},
|
|
349
|
+
}
|
|
350
|
+
: {})}
|
|
351
|
+
minHeight={description ? 62 : 44}
|
|
352
|
+
justifyContent="center"
|
|
353
|
+
paddingHorizontal="$3"
|
|
354
|
+
paddingVertical={description ? "$2" : 0}
|
|
355
|
+
cursor={disabled ? "not-allowed" : "pointer"}
|
|
356
|
+
hoverStyle={disabled ? undefined : { backgroundColor: "$color3" }}
|
|
357
|
+
pressStyle={disabled ? undefined : { backgroundColor: "$color3" }}
|
|
358
|
+
onPress={disabled ? undefined : onPress}
|
|
359
|
+
opacity={disabled ? 0.5 : 1}
|
|
360
|
+
>
|
|
361
|
+
<Text fontSize="$4" lineHeight={25} color="$color12">
|
|
362
|
+
{label}
|
|
363
|
+
</Text>
|
|
364
|
+
{description ? (
|
|
365
|
+
<Text fontSize="$3" lineHeight={24} color={formCommonColors.muted}>
|
|
366
|
+
{description}
|
|
367
|
+
</Text>
|
|
368
|
+
) : null}
|
|
369
|
+
</YStack>
|
|
370
|
+
);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
function StatusRow({ children, testID }: { children: ReactNode; testID?: string }) {
|
|
374
|
+
return (
|
|
375
|
+
<XStack testID={testID} minHeight={44} alignItems="center" paddingHorizontal="$3" role="status">
|
|
376
|
+
<Text fontSize="$4" lineHeight={25} color={formCommonColors.muted}>
|
|
377
|
+
{children}
|
|
378
|
+
</Text>
|
|
379
|
+
</XStack>
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
function SearchResultsList({
|
|
384
|
+
listId,
|
|
385
|
+
status,
|
|
386
|
+
query,
|
|
387
|
+
results,
|
|
388
|
+
pair,
|
|
389
|
+
onLocate,
|
|
390
|
+
onPick,
|
|
391
|
+
onPickPair,
|
|
392
|
+
locateDisabled,
|
|
393
|
+
}: {
|
|
394
|
+
listId: string;
|
|
395
|
+
status: SearchStatus;
|
|
396
|
+
query: string;
|
|
397
|
+
results: GeocodeResult[];
|
|
398
|
+
pair: { lat: number; lng: number } | null;
|
|
399
|
+
onLocate: () => void;
|
|
400
|
+
onPick: (result: GeocodeResult) => void;
|
|
401
|
+
onPickPair: (lat: number, lng: number) => void;
|
|
402
|
+
locateDisabled?: boolean;
|
|
403
|
+
}) {
|
|
404
|
+
const showLocate = status === "idle" || status === "results" || pair != null;
|
|
405
|
+
return (
|
|
406
|
+
<YStack
|
|
407
|
+
testID="geolocation-results"
|
|
408
|
+
id={listId}
|
|
409
|
+
overflow="hidden"
|
|
410
|
+
{...(isWeb
|
|
411
|
+
? // role="listbox" is a valid ARIA role but outside RN's `Role` union;
|
|
412
|
+
// this web-only bag forwards it to the DOM node (house Record cast).
|
|
413
|
+
({ role: "listbox" } as Record<string, unknown>)
|
|
414
|
+
: undefined)}
|
|
415
|
+
>
|
|
416
|
+
{showLocate ? (
|
|
417
|
+
<ResultRow
|
|
418
|
+
testID="geolocation-use-location"
|
|
419
|
+
label={t("Use my current location")}
|
|
420
|
+
onPress={onLocate}
|
|
421
|
+
disabled={locateDisabled}
|
|
422
|
+
/>
|
|
423
|
+
) : null}
|
|
424
|
+
{status === "searching" ? (
|
|
425
|
+
<StatusRow testID="geolocation-search-status">{t("Searching…")}</StatusRow>
|
|
426
|
+
) : null}
|
|
427
|
+
{status === "error" ? (
|
|
428
|
+
<StatusRow testID="geolocation-search-status">
|
|
429
|
+
{t("Search is unavailable — check your connection")}
|
|
430
|
+
</StatusRow>
|
|
431
|
+
) : null}
|
|
432
|
+
{status === "empty" && !pair ? (
|
|
433
|
+
<StatusRow testID="geolocation-search-status">
|
|
434
|
+
{t('No places match "{{query}}"', { query })}
|
|
435
|
+
</StatusRow>
|
|
436
|
+
) : null}
|
|
437
|
+
{pair ? (
|
|
438
|
+
<ResultRow
|
|
439
|
+
testID="geolocation-coord-result"
|
|
440
|
+
label={t("Go to {{pair}}", { pair: formatCoordPair(pair.lat, pair.lng) })}
|
|
441
|
+
description={t("Exact coordinates — no address")}
|
|
442
|
+
onPress={() => onPickPair(pair.lat, pair.lng)}
|
|
443
|
+
/>
|
|
444
|
+
) : null}
|
|
445
|
+
{status === "results"
|
|
446
|
+
? results.map((result, index) => (
|
|
447
|
+
<ResultRow
|
|
448
|
+
key={`${result.lat},${result.lng},${index}`}
|
|
449
|
+
testID={`geolocation-result-${index}`}
|
|
450
|
+
label={result.label}
|
|
451
|
+
description={result.description}
|
|
452
|
+
onPress={() => onPick(result)}
|
|
453
|
+
/>
|
|
454
|
+
))
|
|
455
|
+
: null}
|
|
456
|
+
</YStack>
|
|
457
|
+
);
|
|
458
|
+
}
|
|
274
459
|
|
|
275
460
|
function GeoMapSurface({
|
|
276
461
|
id,
|
|
@@ -293,7 +478,7 @@ function GeoMapSurface({
|
|
|
293
478
|
mapHeight,
|
|
294
479
|
}: {
|
|
295
480
|
id?: string;
|
|
296
|
-
coordinates:
|
|
481
|
+
coordinates: { lat: number; lng: number } | null;
|
|
297
482
|
onCommitCoordinates: (lat: number, lng: number) => void;
|
|
298
483
|
onUseMyLocation: () => void;
|
|
299
484
|
onRetryLocate: () => void;
|
|
@@ -322,13 +507,8 @@ function GeoMapSurface({
|
|
|
322
507
|
const interactive = !disabled && !readOnly;
|
|
323
508
|
const cappedRadius = clampRadiusForLargeComponent(knobProps.borderRadius.borderRadius);
|
|
324
509
|
|
|
325
|
-
// A disabled field opens nothing: the Button below is pointerEvents-none
|
|
326
|
-
// when disabled, but Expand is opened programmatically (AdaptivePopup here
|
|
327
|
-
// takes no `trigger`), so the guard has to live in the handler too.
|
|
328
510
|
const handleExpand = expandIcon ? () => !disabled && setExpanded(true) : undefined;
|
|
329
511
|
|
|
330
|
-
// LC-21 EDITOR: outside/Escape closes; flush any in-progress draft so nothing is lost.
|
|
331
|
-
// Live-valid parses already committed — close is then dismiss-only.
|
|
332
512
|
const handleCoordsOpenChange = (open: boolean) => {
|
|
333
513
|
if (!open) {
|
|
334
514
|
coordsFlushRef.current?.();
|
|
@@ -338,7 +518,7 @@ function GeoMapSurface({
|
|
|
338
518
|
|
|
339
519
|
const handleMapKeyDown = (event: KeyboardEvent) => {
|
|
340
520
|
if (!interactive) return;
|
|
341
|
-
if (event.key === "Enter") {
|
|
521
|
+
if (event.key === "Enter" && hasValue) {
|
|
342
522
|
event.preventDefault();
|
|
343
523
|
setCoordsOpen(true);
|
|
344
524
|
return;
|
|
@@ -366,8 +546,71 @@ function GeoMapSurface({
|
|
|
366
546
|
})
|
|
367
547
|
: null;
|
|
368
548
|
|
|
549
|
+
const coordLine =
|
|
550
|
+
readOnly || disabled ? (
|
|
551
|
+
<XStack
|
|
552
|
+
testID="geolocation-coordline"
|
|
553
|
+
alignItems="center"
|
|
554
|
+
minHeight={COORD_LINE_HEIGHT}
|
|
555
|
+
paddingHorizontal="$2"
|
|
556
|
+
hitSlop={pressTargetHitSlop(COORD_LINE_HEIGHT)}
|
|
557
|
+
>
|
|
558
|
+
<Text fontFamily="$mono" fontSize={12} lineHeight={22} color={formCommonColors.muted}>
|
|
559
|
+
{hasValue ? formatCoordPair(mapLat, mapLng) : t("Enter coordinates")}
|
|
560
|
+
</Text>
|
|
561
|
+
</XStack>
|
|
562
|
+
) : (
|
|
563
|
+
<FloatingPanel
|
|
564
|
+
open={coordsOpen}
|
|
565
|
+
onOpenChange={handleCoordsOpenChange}
|
|
566
|
+
disabled={disabled}
|
|
567
|
+
fitContent={false}
|
|
568
|
+
sizing="fill"
|
|
569
|
+
contentPadding="none"
|
|
570
|
+
trigger={
|
|
571
|
+
<XStack
|
|
572
|
+
testID="geolocation-coordline"
|
|
573
|
+
alignItems="center"
|
|
574
|
+
minHeight={COORD_LINE_HEIGHT}
|
|
575
|
+
paddingHorizontal="$2"
|
|
576
|
+
cursor="pointer"
|
|
577
|
+
{...pressSlopProps(COORD_LINE_HEIGHT, true)}
|
|
578
|
+
hoverStyle={{ backgroundColor: "$color3" }}
|
|
579
|
+
focusVisibleStyle={overlayFocusRing}
|
|
580
|
+
{...(isWeb
|
|
581
|
+
? {
|
|
582
|
+
tabIndex: 0,
|
|
583
|
+
role: "button",
|
|
584
|
+
"aria-label": t("Edit coordinates"),
|
|
585
|
+
onKeyDown: (event: KeyboardEvent) => {
|
|
586
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
587
|
+
event.preventDefault();
|
|
588
|
+
setCoordsOpen(true);
|
|
589
|
+
}
|
|
590
|
+
},
|
|
591
|
+
}
|
|
592
|
+
: {})}
|
|
593
|
+
>
|
|
594
|
+
<Text fontFamily="$mono" fontSize={12} lineHeight={22} color={formCommonColors.muted}>
|
|
595
|
+
{hasValue ? formatCoordPair(mapLat, mapLng) : t("Enter coordinates")}
|
|
596
|
+
</Text>
|
|
597
|
+
</XStack>
|
|
598
|
+
}
|
|
599
|
+
>
|
|
600
|
+
<CoordinatesPanel
|
|
601
|
+
lat={mapLat}
|
|
602
|
+
lng={mapLng}
|
|
603
|
+
knobProps={knobProps}
|
|
604
|
+
required={required}
|
|
605
|
+
hasError={hasError}
|
|
606
|
+
flushRef={coordsFlushRef}
|
|
607
|
+
onCommit={onCommitCoordinates}
|
|
608
|
+
/>
|
|
609
|
+
</FloatingPanel>
|
|
610
|
+
);
|
|
611
|
+
|
|
369
612
|
return (
|
|
370
|
-
|
|
613
|
+
<YStack gap="$2">
|
|
371
614
|
<YStack
|
|
372
615
|
testID="geolocation-map"
|
|
373
616
|
id={id}
|
|
@@ -386,7 +629,7 @@ function GeoMapSurface({
|
|
|
386
629
|
tabIndex: 0,
|
|
387
630
|
role: "application",
|
|
388
631
|
"aria-label": t("Location map"),
|
|
389
|
-
"aria-valuetext": hasValue ?
|
|
632
|
+
"aria-valuetext": hasValue ? formatCoordPair(mapLat, mapLng) : t("No location set"),
|
|
390
633
|
onKeyDown: handleMapKeyDown,
|
|
391
634
|
}
|
|
392
635
|
: {})}
|
|
@@ -403,66 +646,36 @@ function GeoMapSurface({
|
|
|
403
646
|
/>
|
|
404
647
|
)}
|
|
405
648
|
|
|
406
|
-
{
|
|
407
|
-
|
|
408
|
-
|
|
649
|
+
{!hasValue && !locateErrorOpen ? (
|
|
650
|
+
<YStack
|
|
651
|
+
testID="geolocation-empty"
|
|
409
652
|
position="absolute"
|
|
410
|
-
top=
|
|
411
|
-
|
|
653
|
+
top={0}
|
|
654
|
+
right={0}
|
|
655
|
+
bottom={0}
|
|
656
|
+
left={0}
|
|
412
657
|
zIndex={2}
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
{...knobProps.borderRadius}
|
|
658
|
+
alignItems="center"
|
|
659
|
+
justifyContent="center"
|
|
660
|
+
gap="$2"
|
|
661
|
+
pointerEvents="box-none"
|
|
418
662
|
>
|
|
419
|
-
<Text
|
|
420
|
-
|
|
421
|
-
fontSize="$1"
|
|
422
|
-
{...knobProps.body}
|
|
423
|
-
color={formCommonColors.muted}
|
|
424
|
-
>
|
|
425
|
-
{hasValue ? formatCoordChip(mapLat, mapLng) : t("No location set")}
|
|
663
|
+
<Text {...knobProps.body} color={formCommonColors.muted} pointerEvents="none">
|
|
664
|
+
{t("No location set")}
|
|
426
665
|
</Text>
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
disabled={disabled}
|
|
441
|
-
backgroundColor="$background"
|
|
442
|
-
opacity={0.92}
|
|
443
|
-
testID="geolocation-coords-chip"
|
|
444
|
-
aria-label={t("Edit coordinates")}
|
|
445
|
-
focusVisibleStyle={overlayFocusRing}
|
|
446
|
-
onPress={isWeb && !disabled ? () => setCoordsOpen(true) : undefined}
|
|
447
|
-
>
|
|
448
|
-
<Button.Text>
|
|
449
|
-
{hasValue ? formatCoordChip(mapLat, mapLng) : t("Set location")}
|
|
450
|
-
</Button.Text>
|
|
451
|
-
</Button>
|
|
452
|
-
}
|
|
453
|
-
>
|
|
454
|
-
<CoordinatesPanel
|
|
455
|
-
lat={mapLat}
|
|
456
|
-
lng={mapLng}
|
|
457
|
-
knobProps={knobProps}
|
|
458
|
-
required={required}
|
|
459
|
-
hasError={hasError}
|
|
460
|
-
flushRef={coordsFlushRef}
|
|
461
|
-
onCommit={onCommitCoordinates}
|
|
462
|
-
/>
|
|
463
|
-
</FloatingPanel>
|
|
464
|
-
</View>
|
|
465
|
-
)}
|
|
666
|
+
{!readOnly && !disabled ? (
|
|
667
|
+
<Button
|
|
668
|
+
size="$4"
|
|
669
|
+
onPress={onUseMyLocation}
|
|
670
|
+
disabled={locating}
|
|
671
|
+
testID="geolocation-empty-locate"
|
|
672
|
+
aria-label={t("Use my current location")}
|
|
673
|
+
>
|
|
674
|
+
<Button.Text>{t("Use my current location")}</Button.Text>
|
|
675
|
+
</Button>
|
|
676
|
+
) : null}
|
|
677
|
+
</YStack>
|
|
678
|
+
) : null}
|
|
466
679
|
|
|
467
680
|
{handleExpand && expandIcon && (
|
|
468
681
|
<View position="absolute" top="$2" insetInlineEnd="$2" zIndex={2}>
|
|
@@ -472,16 +685,15 @@ function GeoMapSurface({
|
|
|
472
685
|
disabled={disabled}
|
|
473
686
|
onPress={handleExpand}
|
|
474
687
|
backgroundColor="$background"
|
|
475
|
-
opacity={0.92}
|
|
476
688
|
aria-label={t("Expand map")}
|
|
477
689
|
focusVisibleStyle={overlayFocusRing}
|
|
690
|
+
hitSlop={pressTargetHitSlop(32)}
|
|
478
691
|
>
|
|
479
692
|
{expandIcon}
|
|
480
693
|
</Button>
|
|
481
694
|
</View>
|
|
482
695
|
)}
|
|
483
696
|
|
|
484
|
-
{/* Locate overlay (bottom-end) — LC-19 preflight runs on press */}
|
|
485
697
|
{!readOnly && !locateErrorOpen && (
|
|
486
698
|
<View position="absolute" bottom="$2" insetInlineEnd="$2" zIndex={2}>
|
|
487
699
|
<Button
|
|
@@ -490,18 +702,14 @@ function GeoMapSurface({
|
|
|
490
702
|
disabled={disabled || locating}
|
|
491
703
|
onPress={onUseMyLocation}
|
|
492
704
|
backgroundColor="$background"
|
|
493
|
-
opacity={0.92}
|
|
494
705
|
icon={locating ? <Spinner size="small" /> : locationIcon}
|
|
495
706
|
aria-label={locating ? t("Locating") : t("Locate")}
|
|
496
707
|
focusVisibleStyle={overlayFocusRing}
|
|
708
|
+
hitSlop={pressTargetHitSlop(32)}
|
|
497
709
|
/>
|
|
498
710
|
</View>
|
|
499
711
|
)}
|
|
500
712
|
|
|
501
|
-
{/*
|
|
502
|
-
LC-19: locate error stays on the map (Retry + Close) — never
|
|
503
|
-
mount-fail-unmount, never a layout-jumping sibling under the field.
|
|
504
|
-
*/}
|
|
505
713
|
{locateErrorOpen && (
|
|
506
714
|
<YStack
|
|
507
715
|
testID="geolocation-locate-panel"
|
|
@@ -531,6 +739,7 @@ function GeoMapSurface({
|
|
|
531
739
|
</YStack>
|
|
532
740
|
)}
|
|
533
741
|
</YStack>
|
|
742
|
+
{coordLine}
|
|
534
743
|
|
|
535
744
|
{expandIcon && (
|
|
536
745
|
<AdaptivePopup
|
|
@@ -549,7 +758,7 @@ function GeoMapSurface({
|
|
|
549
758
|
/>
|
|
550
759
|
</AdaptivePopup>
|
|
551
760
|
)}
|
|
552
|
-
|
|
761
|
+
</YStack>
|
|
553
762
|
);
|
|
554
763
|
}
|
|
555
764
|
|
|
@@ -564,7 +773,7 @@ export interface GeolocationProps {
|
|
|
564
773
|
required?: boolean;
|
|
565
774
|
size?: SizeTokens;
|
|
566
775
|
id?: string;
|
|
567
|
-
/** JSON string:
|
|
776
|
+
/** JSON string: compact lat/lng or a stock Frappe FeatureCollection. */
|
|
568
777
|
value?: string;
|
|
569
778
|
defaultValue?: string;
|
|
570
779
|
onChange?: (value: string) => void;
|
|
@@ -585,12 +794,65 @@ export interface GeolocationProps {
|
|
|
585
794
|
skeleton?: boolean;
|
|
586
795
|
/** When true, applies compact density (tighter layout gaps; control size unchanged) */
|
|
587
796
|
compact?: boolean;
|
|
797
|
+
/**
|
|
798
|
+
* Forward geocode. Injected — the field never ships a default provider
|
|
799
|
+
* (R4). Without this, search is disabled and the map / locate / coordinate
|
|
800
|
+
* paths stay intact.
|
|
801
|
+
*/
|
|
802
|
+
geocode?: GeocodeFn;
|
|
803
|
+
/** Reverse geocode for a set pin. Injected; the pair is the fallback face. */
|
|
804
|
+
reverseGeocode?: ReverseGeocodeFn;
|
|
805
|
+
/**
|
|
806
|
+
* Convenience bag for `geocode` + `reverseGeocode`. Explicit props win.
|
|
807
|
+
* Any object satisfying `Geocoder` works. No provider is constructed for you.
|
|
808
|
+
*/
|
|
809
|
+
geocoder?: Geocoder;
|
|
588
810
|
}
|
|
589
811
|
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
812
|
+
function CellFace({
|
|
813
|
+
address,
|
|
814
|
+
coordinates,
|
|
815
|
+
onOpen,
|
|
816
|
+
}: {
|
|
817
|
+
address: string | null;
|
|
818
|
+
onOpen?: () => void;
|
|
819
|
+
coordinates: { lat: number; lng: number } | null;
|
|
820
|
+
}) {
|
|
821
|
+
const glyphColor = useGlyphColor();
|
|
822
|
+
const text = address
|
|
823
|
+
? address
|
|
824
|
+
: coordinates
|
|
825
|
+
? formatCoordPair(coordinates.lat, coordinates.lng)
|
|
826
|
+
: t("No location set");
|
|
827
|
+
return (
|
|
828
|
+
<XStack
|
|
829
|
+
testID="geolocation-cell-face"
|
|
830
|
+
{...(isWeb && onOpen
|
|
831
|
+
? {
|
|
832
|
+
tabIndex: 0,
|
|
833
|
+
role: "button",
|
|
834
|
+
"aria-label": t("Edit location"),
|
|
835
|
+
onKeyDown: (event: KeyboardEvent) => {
|
|
836
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
837
|
+
event.preventDefault();
|
|
838
|
+
onOpen();
|
|
839
|
+
}
|
|
840
|
+
},
|
|
841
|
+
}
|
|
842
|
+
: {})}
|
|
843
|
+
alignItems="center"
|
|
844
|
+
gap="$2"
|
|
845
|
+
minHeight={44}
|
|
846
|
+
minWidth={0}
|
|
847
|
+
flex={1}
|
|
848
|
+
>
|
|
849
|
+
<MapPinIcon size={14} color={glyphColor} />
|
|
850
|
+
<Text fontSize="$3" lineHeight={24} color="$color12" numberOfLines={1} flex={1} minWidth={0}>
|
|
851
|
+
{text}
|
|
852
|
+
</Text>
|
|
853
|
+
</XStack>
|
|
854
|
+
);
|
|
855
|
+
}
|
|
594
856
|
|
|
595
857
|
export function Geolocation({
|
|
596
858
|
name,
|
|
@@ -609,25 +871,102 @@ export function Geolocation({
|
|
|
609
871
|
onBlur,
|
|
610
872
|
disabled,
|
|
611
873
|
readOnly,
|
|
612
|
-
locationIcon
|
|
874
|
+
locationIcon,
|
|
613
875
|
expandIcon,
|
|
614
876
|
renderMap,
|
|
615
877
|
skeleton,
|
|
616
878
|
compact,
|
|
879
|
+
geocode: geocodeProp,
|
|
880
|
+
reverseGeocode: reverseGeocodeProp,
|
|
881
|
+
geocoder,
|
|
617
882
|
}: GeolocationProps) {
|
|
618
883
|
const { resolvedForm, knobProps, id } = useFormField({ form: formProp, id: idProp, compact });
|
|
619
884
|
const resolvedValidators = useResolvedValidators(required, validators, label, name);
|
|
885
|
+
const glyphColor = useGlyphColor();
|
|
886
|
+
const resolvedLocationIcon =
|
|
887
|
+
locationIcon === undefined ? (
|
|
888
|
+
<NavigationArrowIcon size={16} color={glyphColor} />
|
|
889
|
+
) : (
|
|
890
|
+
locationIcon
|
|
891
|
+
);
|
|
892
|
+
const inTableCell = useIsInTableCell();
|
|
893
|
+
const viewportGtSm = useViewportGtSm();
|
|
894
|
+
const shouldUseSheet = !isWeb || !viewportGtSm;
|
|
895
|
+
const { geocode, reverseGeocode } = resolveGeocoder({
|
|
896
|
+
geocode: geocodeProp,
|
|
897
|
+
reverseGeocode: reverseGeocodeProp,
|
|
898
|
+
geocoder,
|
|
899
|
+
});
|
|
900
|
+
const searchEnabled = Boolean(geocode) && !disabled && !readOnly;
|
|
901
|
+
const listId = useId();
|
|
902
|
+
|
|
620
903
|
const [locatePhase, setLocatePhase] = useState<LocatePhase>("idle");
|
|
621
904
|
const [locateError, setLocateError] = useState<string | null>(null);
|
|
622
|
-
/** Pre-flight field error (surface never mounts). Cleared on next successful pre-flight. */
|
|
623
905
|
const [preflightError, setPreflightError] = useState<string | undefined>();
|
|
906
|
+
const [address, setAddress] = useState<ResolvedAddress | null>(null);
|
|
907
|
+
const [query, setQuery] = useState("");
|
|
908
|
+
const [resultsOpen, setResultsOpen] = useState(false);
|
|
909
|
+
const [searchStatus, setSearchStatus] = useState<SearchStatus>("idle");
|
|
910
|
+
const [results, setResults] = useState<GeocodeResult[]>([]);
|
|
911
|
+
const [cellOpen, setCellOpen] = useState(false);
|
|
624
912
|
const preflightingRef = useRef(false);
|
|
625
913
|
const onValueChangeRef = useRef<(value: string) => void>(() => {});
|
|
626
914
|
// MPO-100: the value being replaced, so an async locate writes back in the
|
|
627
915
|
// shape the doc arrived in instead of forcing this repo's compact shape.
|
|
628
916
|
const currentValueRef = useRef<string>("");
|
|
917
|
+
const searchGenRef = useRef(0);
|
|
918
|
+
const reverseGenRef = useRef(0);
|
|
919
|
+
const searchRootRef = useRef<HTMLElement | null>(null);
|
|
920
|
+
const searchInputRef = useRef<HTMLInputElement | null>(null);
|
|
921
|
+
const sheetSearchInputRef = useRef<HTMLInputElement | null>(null);
|
|
922
|
+
const closingSearchRef = useRef(false);
|
|
923
|
+
const [nativeSearchFocus] = useState(createNativeSearchFocus);
|
|
924
|
+
// The input owns return focus. FloatingPanel's delayed restore would run
|
|
925
|
+
// onFocus after dismissal and reopen its own results sheet.
|
|
926
|
+
const skipResultsReturnFocusRef = useRef(true);
|
|
927
|
+
const closeSearch = useCallback(
|
|
928
|
+
(restoreFocus = true) => {
|
|
929
|
+
if (!isWeb) nativeSearchFocus.dismiss();
|
|
930
|
+
closingSearchRef.current = true;
|
|
931
|
+
setResultsOpen(false);
|
|
932
|
+
if (isWeb && restoreFocus) searchInputRef.current?.focus({ preventScroll: true });
|
|
933
|
+
closingSearchRef.current = false;
|
|
934
|
+
},
|
|
935
|
+
[nativeSearchFocus],
|
|
936
|
+
);
|
|
937
|
+
const queryIsAddressRef = useRef(true);
|
|
938
|
+
const selectedPlaceRef = useRef<{ key: string; place: GeocodeResult } | null>(null);
|
|
939
|
+
const locateGenRef = useRef(0);
|
|
940
|
+
const interactiveRef = useRef(!disabled && !readOnly);
|
|
941
|
+
interactiveRef.current = !disabled && !readOnly;
|
|
942
|
+
const cancelLocate = useCallback(() => {
|
|
943
|
+
++locateGenRef.current;
|
|
944
|
+
setLocatePhase("idle");
|
|
945
|
+
setLocateError(null);
|
|
946
|
+
}, []);
|
|
947
|
+
useEffect(() => {
|
|
948
|
+
if (disabled || readOnly) cancelLocate();
|
|
949
|
+
}, [disabled, readOnly, cancelLocate]);
|
|
950
|
+
useEffect(
|
|
951
|
+
() => () => {
|
|
952
|
+
++locateGenRef.current;
|
|
953
|
+
++reverseGenRef.current;
|
|
954
|
+
++searchGenRef.current;
|
|
955
|
+
},
|
|
956
|
+
[],
|
|
957
|
+
);
|
|
958
|
+
|
|
959
|
+
const applyAddress = useCallback((next: ResolvedAddress | null, intoQuery: boolean) => {
|
|
960
|
+
setAddress(next);
|
|
961
|
+
if (intoQuery) {
|
|
962
|
+
queryIsAddressRef.current = true;
|
|
963
|
+
setQuery(next?.label ?? "");
|
|
964
|
+
}
|
|
965
|
+
}, []);
|
|
629
966
|
|
|
630
967
|
const requestPosition = useCallback(() => {
|
|
968
|
+
if (!interactiveRef.current) return;
|
|
969
|
+
const gen = ++locateGenRef.current;
|
|
631
970
|
if (!navigator?.geolocation) {
|
|
632
971
|
setLocateError(t("Geolocation unavailable: not supported in this browser."));
|
|
633
972
|
setLocatePhase("error");
|
|
@@ -637,32 +976,35 @@ export function Geolocation({
|
|
|
637
976
|
setLocateError(null);
|
|
638
977
|
navigator.geolocation.getCurrentPosition(
|
|
639
978
|
(position) => {
|
|
979
|
+
if (gen !== locateGenRef.current || !interactiveRef.current) return;
|
|
980
|
+
selectedPlaceRef.current = null;
|
|
981
|
+
queryIsAddressRef.current = true;
|
|
640
982
|
const lat = clampLat(position.coords.latitude);
|
|
641
983
|
const lng = clampLng(position.coords.longitude);
|
|
642
984
|
const previous = currentValueRef.current;
|
|
643
985
|
onValueChangeRef.current(formatGeoValue({ lat, lng }, detectGeoShape(previous), previous));
|
|
644
986
|
setLocatePhase("idle");
|
|
645
987
|
setLocateError(null);
|
|
988
|
+
closeSearch();
|
|
989
|
+
setCellOpen(false);
|
|
646
990
|
},
|
|
647
991
|
(err) => {
|
|
648
|
-
|
|
992
|
+
if (gen !== locateGenRef.current || !interactiveRef.current) return;
|
|
649
993
|
setLocateError(mapGeolocationError(err));
|
|
650
994
|
setLocatePhase("error");
|
|
651
995
|
},
|
|
652
996
|
{ enableHighAccuracy: true, timeout: 10000 },
|
|
653
997
|
);
|
|
654
|
-
}, []);
|
|
998
|
+
}, [closeSearch]);
|
|
655
999
|
|
|
656
|
-
// LC-19 CAPABILITY-PREFLIGHT: verify geolocation availability BEFORE presenting
|
|
657
|
-
// the locate surface. Pre-flight failure renders an inline field error and the
|
|
658
|
-
// surface never mounts. The Locate button stays enabled — pressing it again
|
|
659
|
-
// re-runs the pre-flight.
|
|
660
1000
|
const startLocate = useCallback(async () => {
|
|
661
1001
|
if (disabled || readOnly) return;
|
|
662
1002
|
if (preflightingRef.current || locatePhase === "locating") return;
|
|
663
1003
|
preflightingRef.current = true;
|
|
1004
|
+
const gen = ++locateGenRef.current;
|
|
664
1005
|
try {
|
|
665
1006
|
const result = await preflightGeolocation();
|
|
1007
|
+
if (gen !== locateGenRef.current || !interactiveRef.current) return;
|
|
666
1008
|
if (result.ok) {
|
|
667
1009
|
setPreflightError(undefined);
|
|
668
1010
|
requestPosition();
|
|
@@ -676,6 +1018,102 @@ export function Geolocation({
|
|
|
676
1018
|
}
|
|
677
1019
|
}, [disabled, readOnly, locatePhase, requestPosition]);
|
|
678
1020
|
|
|
1021
|
+
const runReverse = useCallback(
|
|
1022
|
+
async (lat?: number, lng?: number) => {
|
|
1023
|
+
const gen = ++reverseGenRef.current;
|
|
1024
|
+
if (lat === undefined || lng === undefined) {
|
|
1025
|
+
selectedPlaceRef.current = null;
|
|
1026
|
+
applyAddress(null, queryIsAddressRef.current);
|
|
1027
|
+
return;
|
|
1028
|
+
}
|
|
1029
|
+
const key = coordinateKey({ lat, lng });
|
|
1030
|
+
const selected = selectedPlaceRef.current;
|
|
1031
|
+
if (selected?.key === key) {
|
|
1032
|
+
applyAddress({ key, label: formatPlace(selected.place) }, queryIsAddressRef.current);
|
|
1033
|
+
return;
|
|
1034
|
+
}
|
|
1035
|
+
// A new bound pair must not inherit the old address while it resolves.
|
|
1036
|
+
// An independent typed query remains a draft across this transition.
|
|
1037
|
+
applyAddress(null, queryIsAddressRef.current);
|
|
1038
|
+
if (!reverseGeocode) {
|
|
1039
|
+
return;
|
|
1040
|
+
}
|
|
1041
|
+
try {
|
|
1042
|
+
const result = await reverseGeocode({ lat, lng });
|
|
1043
|
+
if (gen !== reverseGenRef.current) return;
|
|
1044
|
+
applyAddress(
|
|
1045
|
+
result ? { key, label: formatPlace(result) } : null,
|
|
1046
|
+
queryIsAddressRef.current,
|
|
1047
|
+
);
|
|
1048
|
+
} catch {
|
|
1049
|
+
if (gen !== reverseGenRef.current) return;
|
|
1050
|
+
applyAddress(null, queryIsAddressRef.current);
|
|
1051
|
+
}
|
|
1052
|
+
},
|
|
1053
|
+
[reverseGeocode, applyAddress],
|
|
1054
|
+
);
|
|
1055
|
+
|
|
1056
|
+
const runSearch = useCallback(
|
|
1057
|
+
async (text: string) => {
|
|
1058
|
+
if (!geocode) return;
|
|
1059
|
+
const gen = ++searchGenRef.current;
|
|
1060
|
+
setSearchStatus("searching");
|
|
1061
|
+
try {
|
|
1062
|
+
const next = await geocode({ query: text });
|
|
1063
|
+
if (gen !== searchGenRef.current) return;
|
|
1064
|
+
setResults(next);
|
|
1065
|
+
setSearchStatus(next.length > 0 ? "results" : "empty");
|
|
1066
|
+
} catch {
|
|
1067
|
+
if (gen !== searchGenRef.current) return;
|
|
1068
|
+
setResults([]);
|
|
1069
|
+
setSearchStatus("error");
|
|
1070
|
+
}
|
|
1071
|
+
},
|
|
1072
|
+
[geocode],
|
|
1073
|
+
);
|
|
1074
|
+
|
|
1075
|
+
useEffect(() => {
|
|
1076
|
+
++searchGenRef.current;
|
|
1077
|
+
if (!searchEnabled || !resultsOpen) return;
|
|
1078
|
+
const trimmed = query.trim();
|
|
1079
|
+
if (queryIsAddressRef.current) return;
|
|
1080
|
+
if (!trimmed) {
|
|
1081
|
+
setSearchStatus("idle");
|
|
1082
|
+
setResults([]);
|
|
1083
|
+
return;
|
|
1084
|
+
}
|
|
1085
|
+
if (parseLatLngPair(trimmed)) {
|
|
1086
|
+
setSearchStatus("idle");
|
|
1087
|
+
setResults([]);
|
|
1088
|
+
return;
|
|
1089
|
+
}
|
|
1090
|
+
const handle = setTimeout(() => {
|
|
1091
|
+
void runSearch(trimmed);
|
|
1092
|
+
}, SEARCH_DEBOUNCE_MS);
|
|
1093
|
+
return () => {
|
|
1094
|
+
clearTimeout(handle);
|
|
1095
|
+
++searchGenRef.current;
|
|
1096
|
+
};
|
|
1097
|
+
}, [query, resultsOpen, searchEnabled, runSearch]);
|
|
1098
|
+
|
|
1099
|
+
useEffect(() => {
|
|
1100
|
+
if (!resultsOpen || shouldUseSheet) return;
|
|
1101
|
+
const onDoc = (event: MouseEvent) => {
|
|
1102
|
+
const root = searchRootRef.current;
|
|
1103
|
+
if (root && event.target instanceof Node && root.contains(event.target)) return;
|
|
1104
|
+
closeSearch(false);
|
|
1105
|
+
};
|
|
1106
|
+
const onKey = (event: globalThis.KeyboardEvent) => {
|
|
1107
|
+
if (event.key === "Escape") closeSearch();
|
|
1108
|
+
};
|
|
1109
|
+
document.addEventListener("mousedown", onDoc);
|
|
1110
|
+
document.addEventListener("keydown", onKey);
|
|
1111
|
+
return () => {
|
|
1112
|
+
document.removeEventListener("mousedown", onDoc);
|
|
1113
|
+
document.removeEventListener("keydown", onKey);
|
|
1114
|
+
};
|
|
1115
|
+
}, [resultsOpen, shouldUseSheet, closeSearch]);
|
|
1116
|
+
|
|
679
1117
|
const makeHandlers = useCallback(
|
|
680
1118
|
(currentValue: string, onValueChange: (value: string) => void) => {
|
|
681
1119
|
onValueChangeRef.current = onValueChange;
|
|
@@ -683,64 +1121,329 @@ export function Geolocation({
|
|
|
683
1121
|
const coordinates = parseGeoValue(currentValue);
|
|
684
1122
|
const shape = detectGeoShape(currentValue);
|
|
685
1123
|
const write = (lat: number, lng: number) => {
|
|
1124
|
+
cancelLocate();
|
|
686
1125
|
onValueChange(
|
|
687
1126
|
formatGeoValue({ lat: clampLat(lat), lng: clampLng(lng) }, shape, currentValue),
|
|
688
1127
|
);
|
|
689
1128
|
};
|
|
690
1129
|
return {
|
|
691
1130
|
coordinates,
|
|
692
|
-
onCommitCoordinates:
|
|
693
|
-
|
|
1131
|
+
onCommitCoordinates: (lat: number, lng: number) => {
|
|
1132
|
+
write(lat, lng);
|
|
1133
|
+
selectedPlaceRef.current = null;
|
|
1134
|
+
queryIsAddressRef.current = true;
|
|
1135
|
+
},
|
|
1136
|
+
onMapChange: (lat: number, lng: number) => {
|
|
1137
|
+
write(lat, lng);
|
|
1138
|
+
selectedPlaceRef.current = null;
|
|
1139
|
+
queryIsAddressRef.current = true;
|
|
1140
|
+
},
|
|
694
1141
|
onUseMyLocation: () => {
|
|
695
1142
|
void startLocate();
|
|
696
1143
|
},
|
|
697
1144
|
onRetryLocate: () => {
|
|
698
1145
|
requestPosition();
|
|
699
1146
|
},
|
|
700
|
-
onCloseLocate:
|
|
701
|
-
|
|
702
|
-
|
|
1147
|
+
onCloseLocate: cancelLocate,
|
|
1148
|
+
onPick: (result: GeocodeResult) => {
|
|
1149
|
+
++reverseGenRef.current;
|
|
1150
|
+
const place = { ...result, lat: clampLat(result.lat), lng: clampLng(result.lng) };
|
|
1151
|
+
selectedPlaceRef.current = { key: coordinateKey(place), place };
|
|
1152
|
+
write(result.lat, result.lng);
|
|
1153
|
+
// A controlled parent may reject or defer the proposal. Keep its
|
|
1154
|
+
// label as a query/candidate until the bound pair actually matches.
|
|
1155
|
+
queryIsAddressRef.current = true;
|
|
1156
|
+
setQuery(formatPlace(result));
|
|
1157
|
+
closeSearch();
|
|
1158
|
+
setCellOpen(false);
|
|
1159
|
+
setSearchStatus("idle");
|
|
1160
|
+
},
|
|
1161
|
+
onPickPair: (lat: number, lng: number) => {
|
|
1162
|
+
write(lat, lng);
|
|
1163
|
+
selectedPlaceRef.current = null;
|
|
1164
|
+
queryIsAddressRef.current = true;
|
|
1165
|
+
closeSearch();
|
|
1166
|
+
setCellOpen(false);
|
|
1167
|
+
setSearchStatus("idle");
|
|
1168
|
+
},
|
|
1169
|
+
onClear: () => {
|
|
1170
|
+
cancelLocate();
|
|
1171
|
+
++searchGenRef.current;
|
|
1172
|
+
++reverseGenRef.current;
|
|
1173
|
+
selectedPlaceRef.current = null;
|
|
1174
|
+
onValueChange("");
|
|
1175
|
+
applyAddress(null, true);
|
|
1176
|
+
setQuery("");
|
|
1177
|
+
setResults([]);
|
|
1178
|
+
setSearchStatus("idle");
|
|
703
1179
|
},
|
|
704
1180
|
};
|
|
705
1181
|
},
|
|
706
|
-
[startLocate, requestPosition],
|
|
1182
|
+
[startLocate, requestPosition, cancelLocate, applyAddress, closeSearch],
|
|
707
1183
|
);
|
|
708
1184
|
|
|
709
1185
|
const mapHeight = getFieldHeight((size ?? knobProps.sizeToken) as SizeTokens, compact ? 4 : 5);
|
|
1186
|
+
const displayError = error || preflightError;
|
|
710
1187
|
|
|
711
|
-
|
|
712
|
-
|
|
1188
|
+
const renderSearch = (
|
|
1189
|
+
handlers: ReturnType<typeof makeHandlers>,
|
|
1190
|
+
opts: { sheetHeader?: boolean; editingCell?: boolean } = {},
|
|
1191
|
+
) => {
|
|
1192
|
+
const showClear = Boolean(query) && !disabled && !readOnly;
|
|
1193
|
+
const inputRef = opts.sheetHeader ? sheetSearchInputRef : searchInputRef;
|
|
1194
|
+
const activateNativeSearch = (focusInput = false) => {
|
|
1195
|
+
nativeSearchFocus.activate();
|
|
1196
|
+
if (!searchEnabled || opts.editingCell) return;
|
|
1197
|
+
const nativeInput = inputRef.current as unknown as {
|
|
1198
|
+
isFocused?: () => boolean;
|
|
1199
|
+
focus: () => void;
|
|
1200
|
+
} | null;
|
|
1201
|
+
// Let first focus open the keyboard before mounting the modal. A restored
|
|
1202
|
+
// input is already focused, so a deliberate new press must open directly.
|
|
1203
|
+
if (nativeInput?.isFocused?.()) setResultsOpen(true);
|
|
1204
|
+
else if (focusInput) nativeInput?.focus();
|
|
1205
|
+
};
|
|
1206
|
+
const clearSearch = () => {
|
|
1207
|
+
handlers.onClear();
|
|
1208
|
+
closingSearchRef.current = true;
|
|
1209
|
+
if (isWeb) inputRef.current?.focus({ preventScroll: true });
|
|
1210
|
+
closingSearchRef.current = false;
|
|
1211
|
+
};
|
|
713
1212
|
return (
|
|
714
|
-
<
|
|
715
|
-
<
|
|
716
|
-
|
|
1213
|
+
<InputParts size={size || knobProps.sizeToken}>
|
|
1214
|
+
<InputParts.Box
|
|
1215
|
+
disabled={disabled || !searchEnabled}
|
|
1216
|
+
// Keep the square Clear target pressable across the frame's border.
|
|
1217
|
+
style={{ overflow: "visible" }}
|
|
1218
|
+
{...(opts.sheetHeader
|
|
1219
|
+
? {
|
|
1220
|
+
borderWidth: 0,
|
|
1221
|
+
borderBottomWidth: 1,
|
|
1222
|
+
borderRadius: 0,
|
|
1223
|
+
}
|
|
1224
|
+
: {})}
|
|
1225
|
+
>
|
|
1226
|
+
<InputParts.Section>
|
|
1227
|
+
<InputParts.Icon adornment="leading">
|
|
1228
|
+
<MagnifyingGlassIcon size={16} />
|
|
1229
|
+
</InputParts.Icon>
|
|
1230
|
+
</InputParts.Section>
|
|
1231
|
+
<InputParts.Section>
|
|
1232
|
+
<InputParts.Area
|
|
1233
|
+
ref={inputRef as never}
|
|
1234
|
+
value={query}
|
|
1235
|
+
disabled={disabled || !searchEnabled}
|
|
1236
|
+
placeholder={t("Search address or place")}
|
|
1237
|
+
aria-label={t("Search for a location")}
|
|
1238
|
+
role="combobox"
|
|
1239
|
+
aria-haspopup="listbox"
|
|
1240
|
+
aria-expanded={resultsOpen}
|
|
1241
|
+
aria-controls={listId}
|
|
1242
|
+
aria-autocomplete="list"
|
|
1243
|
+
data-testid="geolocation-search"
|
|
1244
|
+
onChangeText={(text: string) => {
|
|
1245
|
+
if (!isWeb) nativeSearchFocus.activate();
|
|
1246
|
+
++searchGenRef.current;
|
|
1247
|
+
queryIsAddressRef.current = false;
|
|
1248
|
+
setResults([]);
|
|
1249
|
+
setSearchStatus("idle");
|
|
1250
|
+
setQuery(text);
|
|
1251
|
+
if (searchEnabled) setResultsOpen(true);
|
|
1252
|
+
}}
|
|
1253
|
+
onKeyDown={(event: KeyboardEvent) => {
|
|
1254
|
+
if (event.key === "ArrowDown") {
|
|
1255
|
+
if (searchEnabled) setResultsOpen(true);
|
|
1256
|
+
event.preventDefault();
|
|
1257
|
+
document
|
|
1258
|
+
.getElementById(listId)
|
|
1259
|
+
?.querySelector<HTMLElement>("[role='option']:not([aria-disabled='true'])")
|
|
1260
|
+
?.focus();
|
|
1261
|
+
}
|
|
1262
|
+
}}
|
|
1263
|
+
onClick={() => {
|
|
1264
|
+
if (searchEnabled) setResultsOpen(true);
|
|
1265
|
+
}}
|
|
1266
|
+
{...(!isWeb
|
|
1267
|
+
? {
|
|
1268
|
+
onPressIn: () => activateNativeSearch(),
|
|
1269
|
+
onAccessibilityTap: () => activateNativeSearch(true),
|
|
1270
|
+
onBlur: () => nativeSearchFocus.blur(!!opts.sheetHeader, resultsOpen),
|
|
1271
|
+
}
|
|
1272
|
+
: {})}
|
|
1273
|
+
onFocus={() => {
|
|
1274
|
+
if (
|
|
1275
|
+
searchEnabled &&
|
|
1276
|
+
!opts.editingCell &&
|
|
1277
|
+
!closingSearchRef.current &&
|
|
1278
|
+
(isWeb || nativeSearchFocus.canOpen(!!opts.sheetHeader))
|
|
1279
|
+
)
|
|
1280
|
+
setResultsOpen(true);
|
|
1281
|
+
}}
|
|
1282
|
+
/>
|
|
1283
|
+
</InputParts.Section>
|
|
1284
|
+
{showClear ? (
|
|
1285
|
+
<InputParts.Section>
|
|
1286
|
+
<InputParts.Button
|
|
1287
|
+
glyphRing
|
|
1288
|
+
onPress={clearSearch}
|
|
1289
|
+
onKeyDown={(event: KeyboardEvent) => {
|
|
1290
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
1291
|
+
event.preventDefault();
|
|
1292
|
+
event.stopPropagation();
|
|
1293
|
+
clearSearch();
|
|
1294
|
+
}
|
|
1295
|
+
}}
|
|
1296
|
+
aria-label={t("Clear location")}
|
|
1297
|
+
testID="geolocation-search-clear"
|
|
1298
|
+
>
|
|
1299
|
+
<InputParts.Icon>
|
|
1300
|
+
<XIcon size={14} />
|
|
1301
|
+
</InputParts.Icon>
|
|
1302
|
+
</InputParts.Button>
|
|
1303
|
+
</InputParts.Section>
|
|
1304
|
+
) : null}
|
|
1305
|
+
</InputParts.Box>
|
|
1306
|
+
</InputParts>
|
|
717
1307
|
);
|
|
718
|
-
}
|
|
1308
|
+
};
|
|
719
1309
|
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
1310
|
+
const renderResults = (handlers: ReturnType<typeof makeHandlers>) => (
|
|
1311
|
+
<SearchResultsList
|
|
1312
|
+
listId={listId}
|
|
1313
|
+
status={searchStatus}
|
|
1314
|
+
query={query.trim()}
|
|
1315
|
+
results={results}
|
|
1316
|
+
pair={parseLatLngPair(query)}
|
|
1317
|
+
onLocate={handlers.onUseMyLocation}
|
|
1318
|
+
onPick={handlers.onPick}
|
|
1319
|
+
onPickPair={handlers.onPickPair}
|
|
1320
|
+
locateDisabled={disabled || locatePhase === "locating"}
|
|
1321
|
+
/>
|
|
1322
|
+
);
|
|
1323
|
+
|
|
1324
|
+
const renderFormBody = (
|
|
1325
|
+
currentValue: string,
|
|
1326
|
+
onValueChange: (v: string) => void,
|
|
1327
|
+
editingCell = false,
|
|
1328
|
+
resolvedError: string | boolean | undefined = displayError,
|
|
1329
|
+
): ReactNode => {
|
|
1330
|
+
const handlers = makeHandlers(currentValue, onValueChange);
|
|
1331
|
+
const coordinates = handlers.coordinates;
|
|
1332
|
+
const key = coordinates ? coordinateKey(coordinates) : null;
|
|
1333
|
+
const selected = selectedPlaceRef.current;
|
|
1334
|
+
const displayAddress =
|
|
1335
|
+
(selected?.key === key ? formatPlace(selected.place) : null) ??
|
|
1336
|
+
(address?.key === key ? address.label : null) ??
|
|
1337
|
+
(coordinates ? formatCoordPair(coordinates.lat, coordinates.lng) : null);
|
|
1338
|
+
|
|
1339
|
+
if (inTableCell && !editingCell) {
|
|
1340
|
+
const face = (
|
|
1341
|
+
<CellFace
|
|
1342
|
+
address={displayAddress}
|
|
1343
|
+
coordinates={coordinates}
|
|
1344
|
+
onOpen={!disabled && !readOnly ? () => setCellOpen(true) : undefined}
|
|
1345
|
+
/>
|
|
1346
|
+
);
|
|
1347
|
+
if (readOnly || disabled) return face;
|
|
1348
|
+
return (
|
|
1349
|
+
<FloatingPanel
|
|
1350
|
+
open={cellOpen}
|
|
1351
|
+
onOpenChange={setCellOpen}
|
|
1352
|
+
disabled={disabled}
|
|
1353
|
+
fitContent={false}
|
|
1354
|
+
contentPadding="none"
|
|
1355
|
+
trigger={face}
|
|
1356
|
+
>
|
|
1357
|
+
<YStack minWidth={260} maxWidth={420}>
|
|
1358
|
+
{renderFormBody(currentValue, onValueChange, true, resolvedError)}
|
|
1359
|
+
</YStack>
|
|
1360
|
+
</FloatingPanel>
|
|
1361
|
+
);
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
if (readOnly) {
|
|
1365
|
+
return (
|
|
1366
|
+
<YStack gap="$2">
|
|
1367
|
+
<XStack testID="geolocation-readonly-address" alignItems="center" gap="$2" minHeight={25}>
|
|
1368
|
+
<MapPinIcon size={14} color={glyphColor} />
|
|
1369
|
+
<Text fontSize="$4" lineHeight={25} color="$color12">
|
|
1370
|
+
{displayAddress ?? t("No location set")}
|
|
1371
|
+
</Text>
|
|
1372
|
+
</XStack>
|
|
1373
|
+
<GeoMapSurface
|
|
1374
|
+
id={id}
|
|
1375
|
+
coordinates={coordinates}
|
|
1376
|
+
onCommitCoordinates={handlers.onCommitCoordinates}
|
|
1377
|
+
onUseMyLocation={handlers.onUseMyLocation}
|
|
1378
|
+
onRetryLocate={handlers.onRetryLocate}
|
|
1379
|
+
onCloseLocate={handlers.onCloseLocate}
|
|
1380
|
+
onMapChange={handlers.onMapChange}
|
|
1381
|
+
disabled={!!disabled}
|
|
1382
|
+
readOnly
|
|
1383
|
+
locatePhase={locatePhase}
|
|
1384
|
+
locateError={locateError}
|
|
1385
|
+
knobProps={knobProps}
|
|
1386
|
+
locationIcon={resolvedLocationIcon}
|
|
1387
|
+
expandIcon={expandIcon}
|
|
1388
|
+
renderMap={renderMap}
|
|
1389
|
+
required={required}
|
|
1390
|
+
hasError={!!resolvedError}
|
|
1391
|
+
mapHeight={mapHeight}
|
|
1392
|
+
/>
|
|
1393
|
+
</YStack>
|
|
1394
|
+
);
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
const resultsPanel =
|
|
1398
|
+
resultsOpen && searchEnabled ? (
|
|
1399
|
+
shouldUseSheet ? (
|
|
1400
|
+
<FloatingPanel
|
|
1401
|
+
open={resultsOpen}
|
|
1402
|
+
onOpenChange={(open) => (open ? setResultsOpen(true) : closeSearch())}
|
|
1403
|
+
onNativeShow={() => sheetSearchInputRef.current?.focus()}
|
|
1404
|
+
skipReturnFocusRef={skipResultsReturnFocusRef}
|
|
1405
|
+
sheet
|
|
1406
|
+
sheetFill
|
|
1407
|
+
scrollable
|
|
1408
|
+
contentPadding="none"
|
|
1409
|
+
header={renderSearch(handlers, { sheetHeader: true })}
|
|
1410
|
+
trigger={<View testID="geolocation-sheet-trigger" height={1} width="100%" />}
|
|
1411
|
+
>
|
|
1412
|
+
<YStack testID="geolocation-results-sheet">{renderResults(handlers)}</YStack>
|
|
1413
|
+
</FloatingPanel>
|
|
1414
|
+
) : (
|
|
1415
|
+
<YStack
|
|
1416
|
+
position="absolute"
|
|
1417
|
+
top="100%"
|
|
1418
|
+
left={0}
|
|
1419
|
+
right={0}
|
|
1420
|
+
zIndex={zIndex.dropdown}
|
|
1421
|
+
backgroundColor="$color1"
|
|
1422
|
+
borderWidth={1}
|
|
1423
|
+
borderColor={formInputColors.border.base}
|
|
1424
|
+
borderRadius={knobProps.borderRadius.borderRadius}
|
|
1425
|
+
overflow="hidden"
|
|
1426
|
+
style={{ boxShadow: RESULTS_PANEL_SHADOW }}
|
|
1427
|
+
>
|
|
1428
|
+
<ScrollView maxHeight={mapHeight * 1.5}>{renderResults(handlers)}</ScrollView>
|
|
1429
|
+
</YStack>
|
|
1430
|
+
)
|
|
1431
|
+
) : null;
|
|
723
1432
|
|
|
724
|
-
if (!resolvedForm || !name) {
|
|
725
|
-
const handlers = makeHandlers(value || "", (v) => onChange?.(v));
|
|
726
1433
|
return (
|
|
727
|
-
<
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
// LC-40: FieldLayout owns the dimWhole assembly dim (keepLabel pins
|
|
738
|
-
// the sibling label/helper readable) — it needs the disabled state.
|
|
739
|
-
disabled={disabled}
|
|
740
|
-
>
|
|
1434
|
+
<YStack gap="$2">
|
|
1435
|
+
<YStack
|
|
1436
|
+
ref={searchRootRef as never}
|
|
1437
|
+
position="relative"
|
|
1438
|
+
testID="geolocation-search-anchor"
|
|
1439
|
+
data-sheet={shouldUseSheet ? "true" : "false"}
|
|
1440
|
+
>
|
|
1441
|
+
{renderSearch(handlers, { editingCell })}
|
|
1442
|
+
{resultsPanel}
|
|
1443
|
+
</YStack>
|
|
741
1444
|
<GeoMapSurface
|
|
742
1445
|
id={id}
|
|
743
|
-
coordinates={
|
|
1446
|
+
coordinates={coordinates}
|
|
744
1447
|
onCommitCoordinates={handlers.onCommitCoordinates}
|
|
745
1448
|
onUseMyLocation={handlers.onUseMyLocation}
|
|
746
1449
|
onRetryLocate={handlers.onRetryLocate}
|
|
@@ -751,17 +1454,61 @@ export function Geolocation({
|
|
|
751
1454
|
locatePhase={locatePhase}
|
|
752
1455
|
locateError={locateError}
|
|
753
1456
|
knobProps={knobProps}
|
|
754
|
-
locationIcon={
|
|
1457
|
+
locationIcon={resolvedLocationIcon}
|
|
755
1458
|
expandIcon={expandIcon}
|
|
756
1459
|
renderMap={renderMap}
|
|
757
1460
|
required={required}
|
|
758
|
-
hasError={!!
|
|
1461
|
+
hasError={!!resolvedError}
|
|
759
1462
|
mapHeight={mapHeight}
|
|
760
1463
|
/>
|
|
1464
|
+
</YStack>
|
|
1465
|
+
);
|
|
1466
|
+
};
|
|
1467
|
+
|
|
1468
|
+
if (skeleton) {
|
|
1469
|
+
return (
|
|
1470
|
+
<FieldLayout id={id} label={label} size={size} knobProps={knobProps}>
|
|
1471
|
+
<YStack gap="$2">
|
|
1472
|
+
<Skeleton variant="rounded" width="100%" height={44} />
|
|
1473
|
+
<Skeleton variant="rounded" width="100%" height={mapHeight} />
|
|
1474
|
+
</YStack>
|
|
761
1475
|
</FieldLayout>
|
|
762
1476
|
);
|
|
763
1477
|
}
|
|
764
1478
|
|
|
1479
|
+
const wrap = (
|
|
1480
|
+
currentValue: string,
|
|
1481
|
+
onValueChange: (v: string) => void,
|
|
1482
|
+
resolvedError?: string | boolean,
|
|
1483
|
+
fieldBlur?: (...args: any[]) => void,
|
|
1484
|
+
) => {
|
|
1485
|
+
const coordinates = parseGeoValue(currentValue);
|
|
1486
|
+
const taughtHelper =
|
|
1487
|
+
helperText ??
|
|
1488
|
+
(!coordinates && !readOnly ? t("Search, tap the map, or use your location.") : undefined);
|
|
1489
|
+
return (
|
|
1490
|
+
<FieldLayout
|
|
1491
|
+
id={id}
|
|
1492
|
+
label={label}
|
|
1493
|
+
labelProps={labelProps}
|
|
1494
|
+
error={resolvedError ?? displayError}
|
|
1495
|
+
helperText={inTableCell ? undefined : taughtHelper}
|
|
1496
|
+
required={required}
|
|
1497
|
+
size={size}
|
|
1498
|
+
knobProps={knobProps}
|
|
1499
|
+
onBlur={fieldBlur ?? onBlur}
|
|
1500
|
+
disabled={disabled}
|
|
1501
|
+
>
|
|
1502
|
+
<GeolocationValueSync value={currentValue} runReverse={runReverse} />
|
|
1503
|
+
{renderFormBody(currentValue, onValueChange, false, resolvedError)}
|
|
1504
|
+
</FieldLayout>
|
|
1505
|
+
);
|
|
1506
|
+
};
|
|
1507
|
+
|
|
1508
|
+
if (!resolvedForm || !name) {
|
|
1509
|
+
return wrap(value || "", (v) => onChange?.(v));
|
|
1510
|
+
}
|
|
1511
|
+
|
|
765
1512
|
return (
|
|
766
1513
|
<Field
|
|
767
1514
|
form={resolvedForm}
|
|
@@ -771,47 +1518,32 @@ export function Geolocation({
|
|
|
771
1518
|
>
|
|
772
1519
|
{(field) => {
|
|
773
1520
|
const resolvedError = getFieldError(field, displayError);
|
|
774
|
-
|
|
775
|
-
field.
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
labelProps={labelProps}
|
|
783
|
-
error={resolvedError}
|
|
784
|
-
helperText={helperText}
|
|
785
|
-
required={required}
|
|
786
|
-
size={size}
|
|
787
|
-
knobProps={knobProps}
|
|
788
|
-
onBlur={mergeFieldHandler(field, "handleBlur", onBlur)}
|
|
789
|
-
// LC-40: see standalone branch — FieldLayout carries dimWhole.
|
|
790
|
-
disabled={disabled}
|
|
791
|
-
>
|
|
792
|
-
<GeoMapSurface
|
|
793
|
-
id={id}
|
|
794
|
-
coordinates={handlers.coordinates}
|
|
795
|
-
onCommitCoordinates={handlers.onCommitCoordinates}
|
|
796
|
-
onUseMyLocation={handlers.onUseMyLocation}
|
|
797
|
-
onRetryLocate={handlers.onRetryLocate}
|
|
798
|
-
onCloseLocate={handlers.onCloseLocate}
|
|
799
|
-
onMapChange={handlers.onMapChange}
|
|
800
|
-
disabled={!!disabled}
|
|
801
|
-
readOnly={!!readOnly}
|
|
802
|
-
locatePhase={locatePhase}
|
|
803
|
-
locateError={locateError}
|
|
804
|
-
knobProps={knobProps}
|
|
805
|
-
locationIcon={locationIcon}
|
|
806
|
-
expandIcon={expandIcon}
|
|
807
|
-
renderMap={renderMap}
|
|
808
|
-
required={required}
|
|
809
|
-
hasError={!!resolvedError}
|
|
810
|
-
mapHeight={mapHeight}
|
|
811
|
-
/>
|
|
812
|
-
</FieldLayout>
|
|
1521
|
+
return wrap(
|
|
1522
|
+
(field.state.value as string) || "",
|
|
1523
|
+
(v) => {
|
|
1524
|
+
field.handleChange(v as never);
|
|
1525
|
+
onChange?.(v);
|
|
1526
|
+
},
|
|
1527
|
+
resolvedError,
|
|
1528
|
+
mergeFieldHandler(field, "handleBlur", onBlur),
|
|
813
1529
|
);
|
|
814
1530
|
}}
|
|
815
1531
|
</Field>
|
|
816
1532
|
);
|
|
817
1533
|
}
|
|
1534
|
+
|
|
1535
|
+
function GeolocationValueSync({
|
|
1536
|
+
value,
|
|
1537
|
+
runReverse,
|
|
1538
|
+
}: {
|
|
1539
|
+
value: string;
|
|
1540
|
+
runReverse: (lat?: number, lng?: number) => void;
|
|
1541
|
+
}) {
|
|
1542
|
+
const coords = parseGeoValue(value);
|
|
1543
|
+
const key = coords ? coordinateKey(coords) : "";
|
|
1544
|
+
useEffect(() => {
|
|
1545
|
+
const next = parseGeoValue(value);
|
|
1546
|
+
void runReverse(next?.lat, next?.lng);
|
|
1547
|
+
}, [key, value, runReverse]);
|
|
1548
|
+
return null;
|
|
1549
|
+
}
|