@multiplatform.one/forms 7.6.2 → 7.7.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 +7 -1
- package/dist/cjs/Button/index.native.js +7 -1
- package/dist/cjs/Button/index.native.js.map +1 -1
- package/dist/cjs/Chip/index.cjs +1 -3
- package/dist/cjs/Chip/index.native.js +5 -3
- package/dist/cjs/Chip/index.native.js.map +1 -1
- package/dist/cjs/ContextualSaveBar/index.cjs +19 -3
- package/dist/cjs/ContextualSaveBar/index.native.js +19 -3
- package/dist/cjs/ContextualSaveBar/index.native.js.map +1 -1
- package/dist/cjs/DescriptionList/index.cjs +38 -12
- package/dist/cjs/DescriptionList/index.native.js +38 -12
- package/dist/cjs/DescriptionList/index.native.js.map +1 -1
- package/dist/cjs/ErrorSummary.cjs +4 -1
- package/dist/cjs/ErrorSummary.native.js +4 -1
- package/dist/cjs/ErrorSummary.native.js.map +1 -1
- package/dist/cjs/FloatingPanel/useFloatingPanel.cjs +24 -2
- package/dist/cjs/FloatingPanel/useFloatingPanel.native.js +43 -2
- package/dist/cjs/FloatingPanel/useFloatingPanel.native.js.map +1 -1
- package/dist/cjs/InputParts/index.cjs +42 -2
- package/dist/cjs/InputParts/index.native.js +42 -2
- package/dist/cjs/InputParts/index.native.js.map +1 -1
- package/dist/cjs/fields/Checkbox/Checkbox.cjs +5 -2
- package/dist/cjs/fields/Checkbox/Checkbox.native.js +6 -3
- package/dist/cjs/fields/Checkbox/Checkbox.native.js.map +1 -1
- package/dist/cjs/fields/Checkbox/CheckboxGroup.cjs +69 -59
- package/dist/cjs/fields/Checkbox/CheckboxGroup.native.js +75 -65
- package/dist/cjs/fields/Checkbox/CheckboxGroup.native.js.map +1 -1
- package/dist/cjs/fields/Checkbox/checkboxGroupContext.cjs +33 -0
- package/dist/cjs/fields/Checkbox/checkboxGroupContext.native.js +36 -0
- package/dist/cjs/fields/Checkbox/checkboxGroupContext.native.js.map +1 -0
- package/dist/cjs/fields/DatePicker/DatetimePicker.catalog.cjs +402 -0
- package/dist/cjs/fields/DatePicker/DatetimePicker.catalog.native.js +441 -0
- package/dist/cjs/fields/DatePicker/DatetimePicker.catalog.native.js.map +1 -0
- package/dist/cjs/fields/DatePicker/DatetimePicker.cjs +2 -376
- package/dist/cjs/fields/DatePicker/DatetimePicker.native.js +69 -297
- package/dist/cjs/fields/DatePicker/DatetimePicker.native.js.map +1 -1
- package/dist/cjs/fields/DatePicker/catalog.cjs +306 -0
- package/dist/cjs/fields/DatePicker/catalog.native.js +350 -0
- package/dist/cjs/fields/DatePicker/catalog.native.js.map +1 -0
- package/dist/cjs/fields/DatePicker/index.cjs +7 -280
- package/dist/cjs/fields/DatePicker/index.native.js +66 -185
- package/dist/cjs/fields/DatePicker/index.native.js.map +1 -1
- package/dist/cjs/fields/DatePicker/parts.cjs +12 -9
- package/dist/cjs/fields/DatePicker/parts.native.js +12 -9
- package/dist/cjs/fields/DatePicker/parts.native.js.map +1 -1
- package/dist/cjs/fields/Duration/index.cjs +29 -3
- package/dist/cjs/fields/Duration/index.native.js +29 -3
- package/dist/cjs/fields/Duration/index.native.js.map +1 -1
- package/dist/cjs/fields/PhoneInput/index.cjs +9 -4
- package/dist/cjs/fields/PhoneInput/index.native.js +13 -6
- package/dist/cjs/fields/PhoneInput/index.native.js.map +1 -1
- package/dist/cjs/fields/Rating/index.cjs +1 -1
- package/dist/cjs/fields/Rating/index.native.js +4 -1
- package/dist/cjs/fields/Rating/index.native.js.map +1 -1
- package/dist/cjs/fields/SearchInput/index.cjs +6 -0
- package/dist/cjs/fields/SearchInput/index.native.js +6 -0
- package/dist/cjs/fields/SearchInput/index.native.js.map +1 -1
- package/dist/cjs/fields/Select/index.cjs +75 -2
- package/dist/cjs/fields/Select/index.native.js +77 -2
- package/dist/cjs/fields/Select/index.native.js.map +1 -1
- package/dist/cjs/fields/Signature/index.cjs +9 -5
- package/dist/cjs/fields/Signature/index.native.js +9 -5
- package/dist/cjs/fields/Signature/index.native.js.map +1 -1
- package/dist/cjs/fields/Slider/index.cjs +3 -1
- package/dist/cjs/fields/Slider/index.native.js +3 -1
- package/dist/cjs/fields/Slider/index.native.js.map +1 -1
- package/dist/cjs/fields/Switch/index.cjs +5 -18
- package/dist/cjs/fields/Switch/index.native.js +5 -18
- package/dist/cjs/fields/Switch/index.native.js.map +1 -1
- package/dist/cjs/fields/TextArea/index.cjs +19 -5
- package/dist/cjs/fields/TextArea/index.native.js +23 -5
- package/dist/cjs/fields/TextArea/index.native.js.map +1 -1
- package/dist/cjs/fields/TimePicker/catalog.cjs +203 -0
- package/dist/cjs/fields/TimePicker/catalog.native.js +227 -0
- package/dist/cjs/fields/TimePicker/catalog.native.js.map +1 -0
- package/dist/cjs/fields/TimePicker/index.cjs +6 -177
- package/dist/cjs/fields/TimePicker/index.native.js +74 -85
- package/dist/cjs/fields/TimePicker/index.native.js.map +1 -1
- package/dist/cjs/fields/TimeRangeScrubber/geometry.cjs +6 -0
- package/dist/cjs/fields/TimeRangeScrubber/geometry.native.js +6 -0
- package/dist/cjs/fields/TimeRangeScrubber/geometry.native.js.map +1 -1
- package/dist/cjs/fields/TimeRangeScrubber/index.cjs +34 -14
- package/dist/cjs/fields/TimeRangeScrubber/index.native.js +38 -14
- package/dist/cjs/fields/TimeRangeScrubber/index.native.js.map +1 -1
- package/dist/cjs/fields/nativeDateTime/OsDateTimeSheet.cjs +202 -0
- package/dist/cjs/fields/nativeDateTime/OsDateTimeSheet.native.js +211 -0
- package/dist/cjs/fields/nativeDateTime/OsDateTimeSheet.native.js.map +1 -0
- package/dist/cjs/fields/nativeDateTime/osDateTimeIosDisplay.cjs +30 -0
- package/dist/cjs/fields/nativeDateTime/osDateTimeIosDisplay.native.js +33 -0
- package/dist/cjs/fields/nativeDateTime/osDateTimeIosDisplay.native.js.map +1 -0
- package/dist/cjs/fields/nativeSelect.cjs +30 -0
- package/dist/cjs/fields/nativeSelect.native.js +33 -0
- package/dist/cjs/fields/nativeSelect.native.js.map +1 -0
- package/dist/cjs/index.cjs +1 -0
- package/dist/cjs/index.native.js +1 -0
- package/dist/cjs/index.native.js.map +1 -1
- package/dist/cjs/themedPrimitives.cjs +5 -0
- package/dist/cjs/themedPrimitives.native.js +5 -0
- package/dist/cjs/themedPrimitives.native.js.map +1 -1
- package/dist/esm/Button/index.mjs +8 -2
- package/dist/esm/Button/index.mjs.map +1 -1
- package/dist/esm/Button/index.native.js +8 -2
- package/dist/esm/Button/index.native.js.map +1 -1
- package/dist/esm/Chip/index.mjs +1 -3
- package/dist/esm/Chip/index.mjs.map +1 -1
- package/dist/esm/Chip/index.native.js +5 -3
- package/dist/esm/Chip/index.native.js.map +1 -1
- package/dist/esm/ContextualSaveBar/index.mjs +19 -3
- package/dist/esm/ContextualSaveBar/index.mjs.map +1 -1
- package/dist/esm/ContextualSaveBar/index.native.js +19 -3
- package/dist/esm/ContextualSaveBar/index.native.js.map +1 -1
- package/dist/esm/DescriptionList/index.mjs +38 -12
- package/dist/esm/DescriptionList/index.mjs.map +1 -1
- package/dist/esm/DescriptionList/index.native.js +38 -12
- package/dist/esm/DescriptionList/index.native.js.map +1 -1
- package/dist/esm/ErrorSummary.mjs +5 -2
- package/dist/esm/ErrorSummary.mjs.map +1 -1
- package/dist/esm/ErrorSummary.native.js +5 -2
- package/dist/esm/ErrorSummary.native.js.map +1 -1
- package/dist/esm/FloatingPanel/useFloatingPanel.mjs +24 -3
- package/dist/esm/FloatingPanel/useFloatingPanel.mjs.map +1 -1
- package/dist/esm/FloatingPanel/useFloatingPanel.native.js +43 -3
- package/dist/esm/FloatingPanel/useFloatingPanel.native.js.map +1 -1
- package/dist/esm/InputParts/index.mjs +43 -3
- package/dist/esm/InputParts/index.mjs.map +1 -1
- package/dist/esm/InputParts/index.native.js +43 -3
- package/dist/esm/InputParts/index.native.js.map +1 -1
- package/dist/esm/fields/Checkbox/Checkbox.mjs +5 -2
- package/dist/esm/fields/Checkbox/Checkbox.mjs.map +1 -1
- package/dist/esm/fields/Checkbox/Checkbox.native.js +6 -3
- package/dist/esm/fields/Checkbox/Checkbox.native.js.map +1 -1
- package/dist/esm/fields/Checkbox/CheckboxGroup.mjs +69 -59
- package/dist/esm/fields/Checkbox/CheckboxGroup.mjs.map +1 -1
- package/dist/esm/fields/Checkbox/CheckboxGroup.native.js +75 -65
- package/dist/esm/fields/Checkbox/CheckboxGroup.native.js.map +1 -1
- package/dist/esm/fields/Checkbox/checkboxGroupContext.mjs +7 -0
- package/dist/esm/fields/Checkbox/checkboxGroupContext.mjs.map +1 -0
- package/dist/esm/fields/Checkbox/checkboxGroupContext.native.js +7 -0
- package/dist/esm/fields/Checkbox/checkboxGroupContext.native.js.map +1 -0
- package/dist/esm/fields/DatePicker/DatetimePicker.catalog.mjs +377 -0
- package/dist/esm/fields/DatePicker/DatetimePicker.catalog.mjs.map +1 -0
- package/dist/esm/fields/DatePicker/DatetimePicker.catalog.native.js +413 -0
- package/dist/esm/fields/DatePicker/DatetimePicker.catalog.native.js.map +1 -0
- package/dist/esm/fields/DatePicker/DatetimePicker.mjs +1 -375
- package/dist/esm/fields/DatePicker/DatetimePicker.mjs.map +1 -1
- package/dist/esm/fields/DatePicker/DatetimePicker.native.js +65 -293
- package/dist/esm/fields/DatePicker/DatetimePicker.native.js.map +1 -1
- package/dist/esm/fields/DatePicker/catalog.mjs +276 -0
- package/dist/esm/fields/DatePicker/catalog.mjs.map +1 -0
- package/dist/esm/fields/DatePicker/catalog.native.js +317 -0
- package/dist/esm/fields/DatePicker/catalog.native.js.map +1 -0
- package/dist/esm/fields/DatePicker/index.mjs +1 -274
- package/dist/esm/fields/DatePicker/index.mjs.map +1 -1
- package/dist/esm/fields/DatePicker/index.native.js +60 -179
- package/dist/esm/fields/DatePicker/index.native.js.map +1 -1
- package/dist/esm/fields/DatePicker/parts.mjs +13 -10
- package/dist/esm/fields/DatePicker/parts.mjs.map +1 -1
- package/dist/esm/fields/DatePicker/parts.native.js +13 -10
- package/dist/esm/fields/DatePicker/parts.native.js.map +1 -1
- package/dist/esm/fields/Duration/index.mjs +29 -4
- package/dist/esm/fields/Duration/index.mjs.map +1 -1
- package/dist/esm/fields/Duration/index.native.js +29 -4
- package/dist/esm/fields/Duration/index.native.js.map +1 -1
- package/dist/esm/fields/PhoneInput/index.mjs +9 -4
- package/dist/esm/fields/PhoneInput/index.mjs.map +1 -1
- package/dist/esm/fields/PhoneInput/index.native.js +13 -6
- package/dist/esm/fields/PhoneInput/index.native.js.map +1 -1
- package/dist/esm/fields/Rating/index.mjs +2 -2
- package/dist/esm/fields/Rating/index.mjs.map +1 -1
- package/dist/esm/fields/Rating/index.native.js +5 -2
- package/dist/esm/fields/Rating/index.native.js.map +1 -1
- package/dist/esm/fields/SearchInput/index.mjs +6 -0
- package/dist/esm/fields/SearchInput/index.mjs.map +1 -1
- package/dist/esm/fields/SearchInput/index.native.js +6 -0
- package/dist/esm/fields/SearchInput/index.native.js.map +1 -1
- package/dist/esm/fields/Select/index.mjs +76 -3
- package/dist/esm/fields/Select/index.mjs.map +1 -1
- package/dist/esm/fields/Select/index.native.js +78 -3
- package/dist/esm/fields/Select/index.native.js.map +1 -1
- package/dist/esm/fields/Signature/index.mjs +10 -6
- package/dist/esm/fields/Signature/index.mjs.map +1 -1
- package/dist/esm/fields/Signature/index.native.js +10 -6
- package/dist/esm/fields/Signature/index.native.js.map +1 -1
- package/dist/esm/fields/Slider/index.mjs +4 -2
- package/dist/esm/fields/Slider/index.mjs.map +1 -1
- package/dist/esm/fields/Slider/index.native.js +4 -2
- package/dist/esm/fields/Slider/index.native.js.map +1 -1
- package/dist/esm/fields/Switch/index.mjs +6 -19
- package/dist/esm/fields/Switch/index.mjs.map +1 -1
- package/dist/esm/fields/Switch/index.native.js +6 -19
- package/dist/esm/fields/Switch/index.native.js.map +1 -1
- package/dist/esm/fields/TextArea/index.mjs +19 -5
- package/dist/esm/fields/TextArea/index.mjs.map +1 -1
- package/dist/esm/fields/TextArea/index.native.js +23 -5
- package/dist/esm/fields/TextArea/index.native.js.map +1 -1
- package/dist/esm/fields/TimePicker/catalog.mjs +174 -0
- package/dist/esm/fields/TimePicker/catalog.mjs.map +1 -0
- package/dist/esm/fields/TimePicker/catalog.native.js +195 -0
- package/dist/esm/fields/TimePicker/catalog.native.js.map +1 -0
- package/dist/esm/fields/TimePicker/index.mjs +2 -173
- package/dist/esm/fields/TimePicker/index.mjs.map +1 -1
- package/dist/esm/fields/TimePicker/index.native.js +76 -87
- package/dist/esm/fields/TimePicker/index.native.js.map +1 -1
- package/dist/esm/fields/TimeRangeScrubber/geometry.mjs +6 -1
- package/dist/esm/fields/TimeRangeScrubber/geometry.mjs.map +1 -1
- package/dist/esm/fields/TimeRangeScrubber/geometry.native.js +6 -1
- package/dist/esm/fields/TimeRangeScrubber/geometry.native.js.map +1 -1
- package/dist/esm/fields/TimeRangeScrubber/index.mjs +37 -18
- package/dist/esm/fields/TimeRangeScrubber/index.mjs.map +1 -1
- package/dist/esm/fields/TimeRangeScrubber/index.native.js +41 -18
- package/dist/esm/fields/TimeRangeScrubber/index.native.js.map +1 -1
- package/dist/esm/fields/nativeDateTime/OsDateTimeSheet.mjs +166 -0
- package/dist/esm/fields/nativeDateTime/OsDateTimeSheet.mjs.map +1 -0
- package/dist/esm/fields/nativeDateTime/OsDateTimeSheet.native.js +172 -0
- package/dist/esm/fields/nativeDateTime/OsDateTimeSheet.native.js.map +1 -0
- package/dist/esm/fields/nativeDateTime/osDateTimeIosDisplay.mjs +5 -0
- package/dist/esm/fields/nativeDateTime/osDateTimeIosDisplay.mjs.map +1 -0
- package/dist/esm/fields/nativeDateTime/osDateTimeIosDisplay.native.js +5 -0
- package/dist/esm/fields/nativeDateTime/osDateTimeIosDisplay.native.js.map +1 -0
- package/dist/esm/fields/nativeSelect.mjs +5 -0
- package/dist/esm/fields/nativeSelect.mjs.map +1 -0
- package/dist/esm/fields/nativeSelect.native.js +5 -0
- package/dist/esm/fields/nativeSelect.native.js.map +1 -0
- package/dist/esm/index.mjs +2 -2
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/index.native.js +2 -2
- package/dist/esm/index.native.js.map +1 -1
- package/dist/esm/themedPrimitives.mjs +5 -0
- package/dist/esm/themedPrimitives.mjs.map +1 -1
- package/dist/esm/themedPrimitives.native.js +5 -0
- package/dist/esm/themedPrimitives.native.js.map +1 -1
- package/dist/jsx/Button/index.mjs +8 -2
- package/dist/jsx/Button/index.mjs.map +1 -1
- package/dist/jsx/Button/index.native.js +7 -1
- package/dist/jsx/Button/index.native.js.map +1 -1
- package/dist/jsx/Chip/index.mjs +1 -3
- package/dist/jsx/Chip/index.mjs.map +1 -1
- package/dist/jsx/Chip/index.native.js +5 -3
- package/dist/jsx/Chip/index.native.js.map +1 -1
- package/dist/jsx/ContextualSaveBar/index.mjs +19 -3
- package/dist/jsx/ContextualSaveBar/index.mjs.map +1 -1
- package/dist/jsx/ContextualSaveBar/index.native.js +19 -3
- package/dist/jsx/ContextualSaveBar/index.native.js.map +1 -1
- package/dist/jsx/DescriptionList/index.mjs +38 -12
- package/dist/jsx/DescriptionList/index.mjs.map +1 -1
- package/dist/jsx/DescriptionList/index.native.js +38 -12
- package/dist/jsx/DescriptionList/index.native.js.map +1 -1
- package/dist/jsx/ErrorSummary.mjs +5 -2
- package/dist/jsx/ErrorSummary.mjs.map +1 -1
- package/dist/jsx/ErrorSummary.native.js +4 -1
- package/dist/jsx/ErrorSummary.native.js.map +1 -1
- package/dist/jsx/FloatingPanel/useFloatingPanel.mjs +24 -3
- package/dist/jsx/FloatingPanel/useFloatingPanel.mjs.map +1 -1
- package/dist/jsx/FloatingPanel/useFloatingPanel.native.js +43 -2
- package/dist/jsx/FloatingPanel/useFloatingPanel.native.js.map +1 -1
- package/dist/jsx/InputParts/index.mjs +43 -3
- package/dist/jsx/InputParts/index.mjs.map +1 -1
- package/dist/jsx/InputParts/index.native.js +42 -2
- package/dist/jsx/InputParts/index.native.js.map +1 -1
- package/dist/jsx/fields/Checkbox/Checkbox.mjs +5 -2
- package/dist/jsx/fields/Checkbox/Checkbox.mjs.map +1 -1
- package/dist/jsx/fields/Checkbox/Checkbox.native.js +6 -3
- package/dist/jsx/fields/Checkbox/Checkbox.native.js.map +1 -1
- package/dist/jsx/fields/Checkbox/CheckboxGroup.mjs +69 -59
- package/dist/jsx/fields/Checkbox/CheckboxGroup.mjs.map +1 -1
- package/dist/jsx/fields/Checkbox/CheckboxGroup.native.js +75 -65
- package/dist/jsx/fields/Checkbox/CheckboxGroup.native.js.map +1 -1
- package/dist/jsx/fields/Checkbox/checkboxGroupContext.mjs +7 -0
- package/dist/jsx/fields/Checkbox/checkboxGroupContext.mjs.map +1 -0
- package/dist/jsx/fields/Checkbox/checkboxGroupContext.native.js +36 -0
- package/dist/jsx/fields/Checkbox/checkboxGroupContext.native.js.map +1 -0
- package/dist/jsx/fields/DatePicker/DatetimePicker.catalog.mjs +377 -0
- package/dist/jsx/fields/DatePicker/DatetimePicker.catalog.mjs.map +1 -0
- package/dist/jsx/fields/DatePicker/DatetimePicker.catalog.native.js +441 -0
- package/dist/jsx/fields/DatePicker/DatetimePicker.catalog.native.js.map +1 -0
- package/dist/jsx/fields/DatePicker/DatetimePicker.mjs +1 -375
- package/dist/jsx/fields/DatePicker/DatetimePicker.mjs.map +1 -1
- package/dist/jsx/fields/DatePicker/DatetimePicker.native.js +69 -297
- package/dist/jsx/fields/DatePicker/DatetimePicker.native.js.map +1 -1
- package/dist/jsx/fields/DatePicker/catalog.mjs +276 -0
- package/dist/jsx/fields/DatePicker/catalog.mjs.map +1 -0
- package/dist/jsx/fields/DatePicker/catalog.native.js +350 -0
- package/dist/jsx/fields/DatePicker/catalog.native.js.map +1 -0
- package/dist/jsx/fields/DatePicker/index.mjs +1 -274
- package/dist/jsx/fields/DatePicker/index.mjs.map +1 -1
- package/dist/jsx/fields/DatePicker/index.native.js +66 -185
- package/dist/jsx/fields/DatePicker/index.native.js.map +1 -1
- package/dist/jsx/fields/DatePicker/parts.mjs +13 -10
- package/dist/jsx/fields/DatePicker/parts.mjs.map +1 -1
- package/dist/jsx/fields/DatePicker/parts.native.js +12 -9
- package/dist/jsx/fields/DatePicker/parts.native.js.map +1 -1
- package/dist/jsx/fields/Duration/index.mjs +29 -4
- package/dist/jsx/fields/Duration/index.mjs.map +1 -1
- package/dist/jsx/fields/Duration/index.native.js +29 -3
- package/dist/jsx/fields/Duration/index.native.js.map +1 -1
- package/dist/jsx/fields/PhoneInput/index.mjs +9 -4
- package/dist/jsx/fields/PhoneInput/index.mjs.map +1 -1
- package/dist/jsx/fields/PhoneInput/index.native.js +13 -6
- package/dist/jsx/fields/PhoneInput/index.native.js.map +1 -1
- package/dist/jsx/fields/Rating/index.mjs +2 -2
- package/dist/jsx/fields/Rating/index.mjs.map +1 -1
- package/dist/jsx/fields/Rating/index.native.js +4 -1
- package/dist/jsx/fields/Rating/index.native.js.map +1 -1
- package/dist/jsx/fields/SearchInput/index.mjs +6 -0
- package/dist/jsx/fields/SearchInput/index.mjs.map +1 -1
- package/dist/jsx/fields/SearchInput/index.native.js +6 -0
- package/dist/jsx/fields/SearchInput/index.native.js.map +1 -1
- package/dist/jsx/fields/Select/index.mjs +76 -3
- package/dist/jsx/fields/Select/index.mjs.map +1 -1
- package/dist/jsx/fields/Select/index.native.js +77 -2
- package/dist/jsx/fields/Select/index.native.js.map +1 -1
- package/dist/jsx/fields/Signature/index.mjs +10 -6
- package/dist/jsx/fields/Signature/index.mjs.map +1 -1
- package/dist/jsx/fields/Signature/index.native.js +9 -5
- package/dist/jsx/fields/Signature/index.native.js.map +1 -1
- package/dist/jsx/fields/Slider/index.mjs +4 -2
- package/dist/jsx/fields/Slider/index.mjs.map +1 -1
- package/dist/jsx/fields/Slider/index.native.js +3 -1
- package/dist/jsx/fields/Slider/index.native.js.map +1 -1
- package/dist/jsx/fields/Switch/index.mjs +6 -19
- package/dist/jsx/fields/Switch/index.mjs.map +1 -1
- package/dist/jsx/fields/Switch/index.native.js +5 -18
- package/dist/jsx/fields/Switch/index.native.js.map +1 -1
- package/dist/jsx/fields/TextArea/index.mjs +19 -5
- package/dist/jsx/fields/TextArea/index.mjs.map +1 -1
- package/dist/jsx/fields/TextArea/index.native.js +23 -5
- package/dist/jsx/fields/TextArea/index.native.js.map +1 -1
- package/dist/jsx/fields/TimePicker/catalog.mjs +174 -0
- package/dist/jsx/fields/TimePicker/catalog.mjs.map +1 -0
- package/dist/jsx/fields/TimePicker/catalog.native.js +227 -0
- package/dist/jsx/fields/TimePicker/catalog.native.js.map +1 -0
- package/dist/jsx/fields/TimePicker/index.mjs +2 -173
- package/dist/jsx/fields/TimePicker/index.mjs.map +1 -1
- package/dist/jsx/fields/TimePicker/index.native.js +74 -85
- package/dist/jsx/fields/TimePicker/index.native.js.map +1 -1
- package/dist/jsx/fields/TimeRangeScrubber/geometry.mjs +6 -1
- package/dist/jsx/fields/TimeRangeScrubber/geometry.mjs.map +1 -1
- package/dist/jsx/fields/TimeRangeScrubber/geometry.native.js +6 -0
- package/dist/jsx/fields/TimeRangeScrubber/geometry.native.js.map +1 -1
- package/dist/jsx/fields/TimeRangeScrubber/index.mjs +37 -18
- package/dist/jsx/fields/TimeRangeScrubber/index.mjs.map +1 -1
- package/dist/jsx/fields/TimeRangeScrubber/index.native.js +38 -14
- package/dist/jsx/fields/TimeRangeScrubber/index.native.js.map +1 -1
- package/dist/jsx/fields/nativeDateTime/OsDateTimeSheet.mjs +166 -0
- package/dist/jsx/fields/nativeDateTime/OsDateTimeSheet.mjs.map +1 -0
- package/dist/jsx/fields/nativeDateTime/OsDateTimeSheet.native.js +211 -0
- package/dist/jsx/fields/nativeDateTime/OsDateTimeSheet.native.js.map +1 -0
- package/dist/jsx/fields/nativeDateTime/osDateTimeIosDisplay.mjs +5 -0
- package/dist/jsx/fields/nativeDateTime/osDateTimeIosDisplay.mjs.map +1 -0
- package/dist/jsx/fields/nativeDateTime/osDateTimeIosDisplay.native.js +33 -0
- package/dist/jsx/fields/nativeDateTime/osDateTimeIosDisplay.native.js.map +1 -0
- package/dist/jsx/fields/nativeSelect.mjs +5 -0
- package/dist/jsx/fields/nativeSelect.mjs.map +1 -0
- package/dist/jsx/fields/nativeSelect.native.js +33 -0
- package/dist/jsx/fields/nativeSelect.native.js.map +1 -0
- package/dist/jsx/index.js +2 -2
- package/dist/jsx/index.js.map +1 -1
- package/dist/jsx/index.mjs +2 -2
- package/dist/jsx/index.mjs.map +1 -1
- package/dist/jsx/index.native.js +1 -0
- package/dist/jsx/index.native.js.map +1 -1
- package/dist/jsx/themedPrimitives.mjs +5 -0
- package/dist/jsx/themedPrimitives.mjs.map +1 -1
- package/dist/jsx/themedPrimitives.native.js +5 -0
- package/dist/jsx/themedPrimitives.native.js.map +1 -1
- package/package.json +12 -9
- package/src/Button/Button.labelInk.spec.tsx +1 -1
- package/src/Button/Button.sizeLadder.spec.tsx +124 -0
- package/src/Button/Button.spec.tsx +7 -17
- package/src/Button/Button.stories.tsx +14 -0
- package/src/Button/index.tsx +18 -3
- package/src/Chip/Chip.spec.tsx +15 -0
- package/src/Chip/Chip.stories.tsx +251 -0
- package/src/Chip/index.tsx +5 -1
- package/src/ContextualSaveBar/ContextualSaveBar.spec.tsx +51 -0
- package/src/ContextualSaveBar/ContextualSaveBar.stories.tsx +42 -23
- package/src/ContextualSaveBar/UnsavedChangesDialog.spec.tsx +167 -0
- package/src/ContextualSaveBar/index.tsx +14 -5
- package/src/DescriptionList/DescriptionList.spec.tsx +19 -1
- package/src/DescriptionList/DescriptionList.stories.tsx +5 -1
- package/src/DescriptionList/index.tsx +57 -19
- package/src/ErrorSummary.tsx +5 -2
- package/src/FloatingPanel/panelWidthContract.spec.ts +2 -2
- package/src/FloatingPanel/useFloatingPanel.spec.tsx +199 -1
- package/src/FloatingPanel/useFloatingPanel.ts +45 -3
- package/src/InputParts/index.tsx +56 -2
- package/src/Spinner/SpinnerGlyph.spec.tsx +132 -0
- package/src/Wheel/WheelEdgeFade.spec.tsx +141 -0
- package/src/fields/Checkbox/Checkbox.spec.tsx +24 -0
- package/src/fields/Checkbox/Checkbox.tsx +8 -3
- package/src/fields/Checkbox/CheckboxBox.spec.tsx +395 -0
- package/src/fields/Checkbox/CheckboxCard.spec.tsx +197 -0
- package/src/fields/Checkbox/CheckboxFieldFrame.spec.tsx +116 -0
- package/src/fields/Checkbox/CheckboxGroup.stories.tsx +15 -0
- package/src/fields/Checkbox/CheckboxGroup.tsx +77 -70
- package/src/fields/Checkbox/checkboxGroupContext.ts +12 -0
- package/src/fields/DatePicker/Calendar.spec.tsx +6 -5
- package/src/fields/DatePicker/DatePicker.stories.tsx +13 -0
- package/src/fields/DatePicker/DatetimePicker.catalog.tsx +586 -0
- package/src/fields/DatePicker/DatetimePicker.native.tsx +243 -0
- package/src/fields/DatePicker/DatetimePicker.tsx +2 -584
- package/src/fields/DatePicker/catalog.tsx +450 -0
- package/src/fields/DatePicker/index.native.tsx +261 -0
- package/src/fields/DatePicker/index.tsx +20 -448
- package/src/fields/DatePicker/parts.tsx +11 -6
- package/src/fields/Duration/Duration.spec.tsx +50 -0
- package/src/fields/Duration/Duration.stories.tsx +21 -0
- package/src/fields/Duration/index.tsx +43 -5
- package/src/fields/PhoneInput/PhoneInput.spec.tsx +16 -63
- package/src/fields/PhoneInput/PhoneInput.tvalue-source.spec.ts +23 -0
- package/src/fields/PhoneInput/index.tsx +10 -3
- package/src/fields/Rating/index.tsx +9 -1
- package/src/fields/SearchInput/SearchInput.spec.tsx +20 -0
- package/src/fields/SearchInput/index.tsx +6 -0
- package/src/fields/Select/Select.stories.tsx +23 -1
- package/src/fields/Select/index.tsx +77 -1
- package/src/fields/Signature/Signature.spec.tsx +22 -3
- package/src/fields/Signature/index.tsx +16 -7
- package/src/fields/Slider/index.tsx +4 -1
- package/src/fields/Switch/index.tsx +14 -12
- package/src/fields/TextArea/TextArea.spec.tsx +68 -0
- package/src/fields/TextArea/index.tsx +23 -5
- package/src/fields/TimePicker/TimePicker.stories.tsx +13 -0
- package/src/fields/TimePicker/catalog.tsx +265 -0
- package/src/fields/TimePicker/index.native.tsx +229 -0
- package/src/fields/TimePicker/index.tsx +6 -261
- package/src/fields/TimeRangeScrubber/TimeRangeScrubber.spec.tsx +36 -0
- package/src/fields/TimeRangeScrubber/geometry.ts +13 -0
- package/src/fields/TimeRangeScrubber/index.tsx +49 -8
- package/src/fields/nativeDateTime/OsDateTimeSheet.spec.ts +12 -0
- package/src/fields/nativeDateTime/OsDateTimeSheet.tsx +197 -0
- package/src/fields/nativeDateTime/osDateTimeIosDisplay.ts +4 -0
- package/src/fields/nativeSelect.spec.ts +28 -0
- package/src/fields/nativeSelect.ts +13 -0
- package/src/index.ts +2 -2
- package/src/themedPrimitives.tsx +10 -6
- package/types/Button/index.d.ts +3 -2
- package/types/Button/index.d.ts.map +1 -1
- package/types/Chip/index.d.ts.map +1 -1
- package/types/ContextualSaveBar/index.d.ts.map +1 -1
- package/types/DescriptionList/index.d.ts.map +1 -1
- package/types/ErrorSummary.d.ts.map +1 -1
- package/types/FloatingPanel/useFloatingPanel.d.ts +13 -0
- package/types/FloatingPanel/useFloatingPanel.d.ts.map +1 -1
- package/types/InputParts/index.d.ts.map +1 -1
- package/types/fields/Checkbox/Checkbox.d.ts.map +1 -1
- package/types/fields/Checkbox/CheckboxGroup.d.ts.map +1 -1
- package/types/fields/Checkbox/checkboxGroupContext.d.ts +8 -0
- package/types/fields/Checkbox/checkboxGroupContext.d.ts.map +1 -0
- package/types/fields/DatePicker/DatetimePicker.catalog.d.ts +35 -0
- package/types/fields/DatePicker/DatetimePicker.catalog.d.ts.map +1 -0
- package/types/fields/DatePicker/DatetimePicker.d.ts +2 -32
- package/types/fields/DatePicker/DatetimePicker.d.ts.map +1 -1
- package/types/fields/DatePicker/DatetimePicker.native.d.ts +26 -0
- package/types/fields/DatePicker/DatetimePicker.native.d.ts.map +1 -0
- package/types/fields/DatePicker/catalog.d.ts +42 -0
- package/types/fields/DatePicker/catalog.d.ts.map +1 -0
- package/types/fields/DatePicker/index.d.ts +2 -39
- package/types/fields/DatePicker/index.d.ts.map +1 -1
- package/types/fields/DatePicker/index.native.d.ts +40 -0
- package/types/fields/DatePicker/index.native.d.ts.map +1 -0
- package/types/fields/DatePicker/parts.d.ts.map +1 -1
- package/types/fields/Duration/index.d.ts +2 -0
- package/types/fields/Duration/index.d.ts.map +1 -1
- package/types/fields/PhoneInput/index.d.ts.map +1 -1
- package/types/fields/Rating/index.d.ts.map +1 -1
- package/types/fields/SearchInput/index.d.ts.map +1 -1
- package/types/fields/Select/index.d.ts +6 -1
- package/types/fields/Select/index.d.ts.map +1 -1
- package/types/fields/Signature/index.d.ts.map +1 -1
- package/types/fields/Slider/index.d.ts.map +1 -1
- package/types/fields/Switch/index.d.ts.map +1 -1
- package/types/fields/TextArea/index.d.ts.map +1 -1
- package/types/fields/TimePicker/catalog.d.ts +41 -0
- package/types/fields/TimePicker/catalog.d.ts.map +1 -0
- package/types/fields/TimePicker/index.d.ts +2 -38
- package/types/fields/TimePicker/index.d.ts.map +1 -1
- package/types/fields/TimePicker/index.native.d.ts +31 -0
- package/types/fields/TimePicker/index.native.d.ts.map +1 -0
- package/types/fields/TimeRangeScrubber/geometry.d.ts +8 -0
- package/types/fields/TimeRangeScrubber/geometry.d.ts.map +1 -1
- package/types/fields/TimeRangeScrubber/index.d.ts +1 -1
- package/types/fields/TimeRangeScrubber/index.d.ts.map +1 -1
- package/types/fields/nativeDateTime/OsDateTimeSheet.d.ts +12 -0
- package/types/fields/nativeDateTime/OsDateTimeSheet.d.ts.map +1 -0
- package/types/fields/nativeDateTime/osDateTimeIosDisplay.d.ts +3 -0
- package/types/fields/nativeDateTime/osDateTimeIosDisplay.d.ts.map +1 -0
- package/types/fields/nativeSelect.d.ts +12 -0
- package/types/fields/nativeSelect.d.ts.map +1 -0
- package/types/index.d.ts +2 -2
- package/types/index.d.ts.map +1 -1
- package/types/themedPrimitives.d.ts.map +1 -1
|
@@ -15,9 +15,10 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import { waitFor } from "@testing-library/react";
|
|
17
17
|
import { renderWithProviders } from "@multiplatform.one/test-utils";
|
|
18
|
-
import { describe, expect, it } from "vitest";
|
|
18
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
19
19
|
import {
|
|
20
20
|
type PanelWidthMode,
|
|
21
|
+
measureDrawnRadius,
|
|
21
22
|
panelAnchorOffset,
|
|
22
23
|
resolvePanelWidthMode,
|
|
23
24
|
useFloatingPanel,
|
|
@@ -166,3 +167,200 @@ describe("FloatingPanel width modes (MPO-38)", () => {
|
|
|
166
167
|
expect(result.getByTestId("anchor-floating").style.minWidth).toBe("");
|
|
167
168
|
});
|
|
168
169
|
});
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Drawn-radius equality (MPO-129).
|
|
173
|
+
*
|
|
174
|
+
* The panel wears the arc the TRIGGER actually draws, and the box the panel
|
|
175
|
+
* anchors to is not always the box that paints. `FloatingPanelWeb` hands
|
|
176
|
+
* `setReferenceRef` to a layout-only `<View>` around the caller's `trigger`
|
|
177
|
+
* slot, so reading the anchor's own computed radius returned 0px and
|
|
178
|
+
* `min(0, h/2, w/2)` pinned every consumer's panel to a square corner at
|
|
179
|
+
* EVERY stop — measured on origin/main 3bb889f32: trigger 5/9/16/50px,
|
|
180
|
+
* panel 0px, in light and dark.
|
|
181
|
+
*
|
|
182
|
+
* These cases lock the three behaviours that fix depends on, so the zero
|
|
183
|
+
* cannot come back: the anchor's own paint wins when it has one (the
|
|
184
|
+
* Select/Combobox path, which was never broken), an unpainted anchor
|
|
185
|
+
* descends to the box sharing its corner, and an anchor with nothing painted
|
|
186
|
+
* anywhere returns null so the caller falls back to the CLAMPED TOKEN rather
|
|
187
|
+
* than to zero.
|
|
188
|
+
*
|
|
189
|
+
* FIXTURE FIDELITY, and it is load-bearing. jsdom reports an UNSET
|
|
190
|
+
* `borderTopLeftRadius` as the empty string; a browser reports "0px". The old
|
|
191
|
+
* implementation ran `parseFloat` on that value, so on an unstyled jsdom node
|
|
192
|
+
* it saw NaN and returned null — accidentally the right answer — while in a
|
|
193
|
+
* browser it saw 0 and returned a measured 0 that beat the token fallback.
|
|
194
|
+
* That is the bug. So every layout-only wrapper below sets
|
|
195
|
+
* `borderTopLeftRadius: "0px"` explicitly: without it these cases pass against
|
|
196
|
+
* the broken implementation and lock nothing.
|
|
197
|
+
*/
|
|
198
|
+
function boxed(
|
|
199
|
+
el: HTMLElement,
|
|
200
|
+
rect: { x: number; y: number; width: number; height: number },
|
|
201
|
+
style: Partial<CSSStyleDeclaration>,
|
|
202
|
+
) {
|
|
203
|
+
mockRect(el, rect);
|
|
204
|
+
Object.assign(el.style, style);
|
|
205
|
+
return el;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/** jsdom resolves computed style only for ATTACHED nodes, and the fix reads
|
|
209
|
+
* computed style — so the fixture has to live in the document. */
|
|
210
|
+
function mounted(el: HTMLElement) {
|
|
211
|
+
document.body.appendChild(el);
|
|
212
|
+
return el;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
describe("FloatingPanel drawn radius (MPO-129)", () => {
|
|
216
|
+
afterEach(() => {
|
|
217
|
+
document.body.innerHTML = "";
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
it("reads the anchor itself when the anchor is the painted control", () => {
|
|
221
|
+
// Select and Combobox spread the reference onto InputParts.Box. That path
|
|
222
|
+
// was correct before this fix and must stay byte-for-byte correct after.
|
|
223
|
+
const anchor = mounted(
|
|
224
|
+
boxed(
|
|
225
|
+
document.createElement("div"),
|
|
226
|
+
{ x: 0, y: 0, width: 240, height: 44 },
|
|
227
|
+
{
|
|
228
|
+
borderTopLeftRadius: "9px",
|
|
229
|
+
backgroundColor: "rgb(255, 255, 255)",
|
|
230
|
+
},
|
|
231
|
+
),
|
|
232
|
+
);
|
|
233
|
+
expect(measureDrawnRadius(anchor)).toBe(9);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
it("descends to the painted trigger when the anchor is a layout-only wrapper", () => {
|
|
237
|
+
// The regression itself: wrapper draws nothing, Button draws 16px.
|
|
238
|
+
const wrapper = mounted(
|
|
239
|
+
boxed(
|
|
240
|
+
document.createElement("div"),
|
|
241
|
+
{ x: 0, y: 0, width: 240, height: 44 },
|
|
242
|
+
{
|
|
243
|
+
// A browser reports "0px" here; jsdom leaves it "". Without this the
|
|
244
|
+
// fixture is not the box the bug happens on — see the note above.
|
|
245
|
+
borderTopLeftRadius: "0px",
|
|
246
|
+
cursor: "pointer",
|
|
247
|
+
opacity: "1",
|
|
248
|
+
},
|
|
249
|
+
),
|
|
250
|
+
);
|
|
251
|
+
const button = boxed(
|
|
252
|
+
document.createElement("div"),
|
|
253
|
+
{ x: 0, y: 0, width: 240, height: 44 },
|
|
254
|
+
{
|
|
255
|
+
borderTopLeftRadius: "16px",
|
|
256
|
+
backgroundColor: "rgb(240, 240, 240)",
|
|
257
|
+
},
|
|
258
|
+
);
|
|
259
|
+
wrapper.appendChild(button);
|
|
260
|
+
expect(measureDrawnRadius(wrapper)).toBe(16);
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
it("clamps the descended radius to the painted trigger's own half-height", () => {
|
|
264
|
+
// `full` asks for 50px on a 44px control; CSS draws 22px, so the panel
|
|
265
|
+
// must draw 22px too — matching a 50px arc would not coincide.
|
|
266
|
+
const wrapper = mounted(
|
|
267
|
+
boxed(
|
|
268
|
+
document.createElement("div"),
|
|
269
|
+
{ x: 0, y: 0, width: 240, height: 44 },
|
|
270
|
+
{
|
|
271
|
+
// A browser reports "0px" here; jsdom leaves it "". Without this the
|
|
272
|
+
// fixture is not the box the bug happens on — see the note above.
|
|
273
|
+
borderTopLeftRadius: "0px",
|
|
274
|
+
cursor: "pointer",
|
|
275
|
+
},
|
|
276
|
+
),
|
|
277
|
+
);
|
|
278
|
+
const button = boxed(
|
|
279
|
+
document.createElement("div"),
|
|
280
|
+
{ x: 0, y: 0, width: 240, height: 44 },
|
|
281
|
+
{
|
|
282
|
+
borderTopLeftRadius: "50px",
|
|
283
|
+
backgroundColor: "rgb(240, 240, 240)",
|
|
284
|
+
},
|
|
285
|
+
);
|
|
286
|
+
wrapper.appendChild(button);
|
|
287
|
+
expect(measureDrawnRadius(wrapper)).toBe(22);
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
it("keeps a square corner square rather than inventing one", () => {
|
|
291
|
+
const wrapper = mounted(
|
|
292
|
+
boxed(
|
|
293
|
+
document.createElement("div"),
|
|
294
|
+
{ x: 0, y: 0, width: 240, height: 44 },
|
|
295
|
+
{
|
|
296
|
+
// A browser reports "0px" here; jsdom leaves it "". Without this the
|
|
297
|
+
// fixture is not the box the bug happens on — see the note above.
|
|
298
|
+
borderTopLeftRadius: "0px",
|
|
299
|
+
cursor: "pointer",
|
|
300
|
+
},
|
|
301
|
+
),
|
|
302
|
+
);
|
|
303
|
+
const button = boxed(
|
|
304
|
+
document.createElement("div"),
|
|
305
|
+
{ x: 0, y: 0, width: 240, height: 44 },
|
|
306
|
+
{
|
|
307
|
+
borderTopLeftRadius: "0px",
|
|
308
|
+
backgroundColor: "rgb(240, 240, 240)",
|
|
309
|
+
},
|
|
310
|
+
);
|
|
311
|
+
wrapper.appendChild(button);
|
|
312
|
+
expect(measureDrawnRadius(wrapper)).toBe(0);
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
it("ignores a painted descendant that does not share the anchor's corner", () => {
|
|
316
|
+
// An inset swatch is not the corner the panel meets. Nothing else paints,
|
|
317
|
+
// so this resolves to the token fallback, not to the swatch's radius.
|
|
318
|
+
const wrapper = mounted(
|
|
319
|
+
boxed(
|
|
320
|
+
document.createElement("div"),
|
|
321
|
+
{ x: 0, y: 0, width: 240, height: 44 },
|
|
322
|
+
{
|
|
323
|
+
// A browser reports "0px" here; jsdom leaves it "". Without this the
|
|
324
|
+
// fixture is not the box the bug happens on — see the note above.
|
|
325
|
+
borderTopLeftRadius: "0px",
|
|
326
|
+
cursor: "pointer",
|
|
327
|
+
},
|
|
328
|
+
),
|
|
329
|
+
);
|
|
330
|
+
const swatch = boxed(
|
|
331
|
+
document.createElement("div"),
|
|
332
|
+
{ x: 12, y: 12, width: 20, height: 20 },
|
|
333
|
+
{
|
|
334
|
+
borderTopLeftRadius: "10px",
|
|
335
|
+
backgroundColor: "rgb(255, 0, 0)",
|
|
336
|
+
},
|
|
337
|
+
);
|
|
338
|
+
wrapper.appendChild(swatch);
|
|
339
|
+
expect(measureDrawnRadius(wrapper)).toBeNull();
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
it("returns null — never 0 — when nothing under the anchor paints a corner", () => {
|
|
343
|
+
// This is the whole bug in one assertion. A measured 0 wins over the
|
|
344
|
+
// token fallback because `measuredRadius ?? clampDropdownRadius(token)`
|
|
345
|
+
// only falls back on null, so returning 0 here squares every panel.
|
|
346
|
+
const wrapper = mounted(
|
|
347
|
+
boxed(
|
|
348
|
+
document.createElement("div"),
|
|
349
|
+
{ x: 0, y: 0, width: 240, height: 44 },
|
|
350
|
+
{
|
|
351
|
+
// A browser reports "0px" here; jsdom leaves it "". Without this the
|
|
352
|
+
// fixture is not the box the bug happens on — see the note above.
|
|
353
|
+
borderTopLeftRadius: "0px",
|
|
354
|
+
cursor: "pointer",
|
|
355
|
+
},
|
|
356
|
+
),
|
|
357
|
+
);
|
|
358
|
+
wrapper.appendChild(
|
|
359
|
+
boxed(document.createElement("span"), { x: 0, y: 0, width: 60, height: 20 }, {}),
|
|
360
|
+
);
|
|
361
|
+
// Guard the premise: if this ever reads "" again the case below goes green
|
|
362
|
+
// against a broken implementation, exactly as it did before MPO-129.
|
|
363
|
+
expect(getComputedStyle(wrapper).borderTopLeftRadius).toBe("0px");
|
|
364
|
+
expect(measureDrawnRadius(wrapper)).toBeNull();
|
|
365
|
+
});
|
|
366
|
+
});
|
|
@@ -141,6 +141,42 @@ export interface UseFloatingPanelReturn {
|
|
|
141
141
|
|
|
142
142
|
// ── Hook ──────────────────────────────────────────────────────
|
|
143
143
|
|
|
144
|
+
/** A box paints a corner if it draws a radius, a border or a background. */
|
|
145
|
+
function paintsACorner(el: HTMLElement): boolean {
|
|
146
|
+
const cs = getComputedStyle(el);
|
|
147
|
+
if (Number.parseFloat(cs.borderTopLeftRadius) > 0) return true;
|
|
148
|
+
if (Number.parseFloat(cs.borderTopWidth) > 0 && cs.borderTopStyle !== "none") return true;
|
|
149
|
+
const bg = cs.backgroundColor;
|
|
150
|
+
return !!bg && bg !== "transparent" && bg !== "rgba(0, 0, 0, 0)";
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* The element the panel ANCHORS to is not always the element that PAINTS
|
|
155
|
+
* (MPO-129). `FloatingPanelWeb` hands `setReferenceRef` to a layout-only
|
|
156
|
+
* `<View>` wrapping the caller's `trigger` slot — it carries cursor and
|
|
157
|
+
* opacity and draws nothing — so reading its computed radius returns 0px and
|
|
158
|
+
* `min(0, h/2, w/2)` pins the panel to a square corner at EVERY stop, not
|
|
159
|
+
* just at `full`. Select and Combobox escape it only because they spread the
|
|
160
|
+
* reference onto `InputParts.Box`, which is the painted control itself.
|
|
161
|
+
*
|
|
162
|
+
* Positioning still measures the wrapper (that is the cover contract's box —
|
|
163
|
+
* a ColorPicker read from its swatch would measure the swatch's inset).
|
|
164
|
+
* Only the ARC descends: to the outermost box that shares the anchor's own
|
|
165
|
+
* top-left corner and actually paints one. Nothing painted → null, so the
|
|
166
|
+
* caller falls back to the clamped token rather than to zero.
|
|
167
|
+
*/
|
|
168
|
+
function paintedCornerBox(root: HTMLElement): HTMLElement | null {
|
|
169
|
+
if (paintsACorner(root)) return root;
|
|
170
|
+
const rootRect = root.getBoundingClientRect();
|
|
171
|
+
for (const el of Array.from(root.querySelectorAll<HTMLElement>("*"))) {
|
|
172
|
+
const rect = el.getBoundingClientRect();
|
|
173
|
+
if (rect.width <= 0 || rect.height <= 0) continue;
|
|
174
|
+
if (Math.abs(rect.left - rootRect.left) > 1 || Math.abs(rect.top - rootRect.top) > 1) continue;
|
|
175
|
+
if (paintsACorner(el)) return el;
|
|
176
|
+
}
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
|
|
144
180
|
/**
|
|
145
181
|
* OVERLAY_ANCHOR_GAP rule — radius equality: a panel that aligns to (or
|
|
146
182
|
* covers) its trigger must draw the SAME corner arc as the trigger at the
|
|
@@ -149,11 +185,17 @@ export interface UseFloatingPanelReturn {
|
|
|
149
185
|
* ~22px arcs on a 44px-high field while a token-styled panel would draw its
|
|
150
186
|
* own, different arc. Measure the trigger's effective (drawn) radius
|
|
151
187
|
* instead and hand that px value to the panel.
|
|
188
|
+
*
|
|
189
|
+
* Exported for `useFloatingPanel.spec.tsx`: the arc is measured, not
|
|
190
|
+
* declared, so the only way to lock it is to feed it real boxes.
|
|
152
191
|
*/
|
|
153
|
-
function measureDrawnRadius(el: HTMLElement): number | null {
|
|
154
|
-
const
|
|
192
|
+
export function measureDrawnRadius(el: HTMLElement): number | null {
|
|
193
|
+
const box = paintedCornerBox(el);
|
|
194
|
+
if (!box) return null;
|
|
195
|
+
const raw = parseFloat(getComputedStyle(box).borderTopLeftRadius);
|
|
155
196
|
if (!Number.isFinite(raw)) return null;
|
|
156
|
-
const rect =
|
|
197
|
+
const rect = box.getBoundingClientRect();
|
|
198
|
+
if (rect.width <= 0 || rect.height <= 0) return null;
|
|
157
199
|
return Math.min(raw, rect.height / 2, rect.width / 2);
|
|
158
200
|
}
|
|
159
201
|
|
package/src/InputParts/index.tsx
CHANGED
|
@@ -27,6 +27,8 @@ import {
|
|
|
27
27
|
import {
|
|
28
28
|
ensureCompositeFocusRing,
|
|
29
29
|
ensureFocusVisibleRing,
|
|
30
|
+
FOCUS_RING_CLIPPED_OFFSET,
|
|
31
|
+
FOCUS_RING_HALO_OFFSET,
|
|
30
32
|
isTouchSurface,
|
|
31
33
|
recipeFamilies,
|
|
32
34
|
resolveGlyphPaint,
|
|
@@ -219,7 +221,9 @@ export function boxRingMode(
|
|
|
219
221
|
}
|
|
220
222
|
|
|
221
223
|
export function boxRingOffset(mode: "inset" | "outset" | undefined): number {
|
|
222
|
-
|
|
224
|
+
// Both branches are LC-71 §4 sanctioned offsets: the clipping inset when the
|
|
225
|
+
// Box clips its adornments, the shape halo when it does not (MPO-92).
|
|
226
|
+
return mode === "outset" ? FOCUS_RING_HALO_OFFSET : FOCUS_RING_CLIPPED_OFFSET;
|
|
223
227
|
}
|
|
224
228
|
|
|
225
229
|
export const inputPartsRingCss = `/* LC-71: ring on Box (adornments inside); Area never outlines.
|
|
@@ -781,6 +785,13 @@ const InputAreaImpl = InputFrame.styleable<{
|
|
|
781
785
|
focusVisibleStyle: focusVisibleStyleProp,
|
|
782
786
|
className: classNameProp,
|
|
783
787
|
inputMode: inputModeProp,
|
|
788
|
+
flex: flexProp,
|
|
789
|
+
width: widthProp,
|
|
790
|
+
minWidth: minWidthProp,
|
|
791
|
+
maxWidth: maxWidthProp,
|
|
792
|
+
height: heightProp,
|
|
793
|
+
minHeight: minHeightProp,
|
|
794
|
+
maxHeight: maxHeightProp,
|
|
784
795
|
...props
|
|
785
796
|
} = areaProps as typeof areaProps & {
|
|
786
797
|
focusStyle?: Record<string, unknown>;
|
|
@@ -788,6 +799,13 @@ const InputAreaImpl = InputFrame.styleable<{
|
|
|
788
799
|
className?: string;
|
|
789
800
|
inputMode?: InputModeValue;
|
|
790
801
|
type?: string;
|
|
802
|
+
flex?: number | string;
|
|
803
|
+
width?: number | string;
|
|
804
|
+
minWidth?: number | string;
|
|
805
|
+
maxWidth?: number | string;
|
|
806
|
+
height?: number | string;
|
|
807
|
+
minHeight?: number | string;
|
|
808
|
+
maxHeight?: number | string;
|
|
791
809
|
};
|
|
792
810
|
const { knobProps } = useResolvedKnobs();
|
|
793
811
|
const theme = useTheme();
|
|
@@ -799,6 +817,11 @@ const InputAreaImpl = InputFrame.styleable<{
|
|
|
799
817
|
// number-pad↔numeric, decimal-pad↔decimal, email-address↔email.
|
|
800
818
|
const resolvedInputMode = inputModeProp ?? mapKeyboardTypeToInputMode(keyboardType);
|
|
801
819
|
const resolvedKeyboardType = keyboardType ?? mapInputModeToKeyboardType(resolvedInputMode);
|
|
820
|
+
// Duration (and any Area with a numeric width / flex 0) must not sit inside
|
|
821
|
+
// a flex:1 wrapper — that wrapper grows with the Box, then Section's
|
|
822
|
+
// justifyContent:center floats the cluster in the leftover space. OTP still
|
|
823
|
+
// pins width on Box, not Area, so it keeps the grow wrapper.
|
|
824
|
+
const hugWidth = flexProp === 0 || typeof widthProp === "number";
|
|
802
825
|
|
|
803
826
|
return (
|
|
804
827
|
// LC-06/LC-10 (UX-P12): in a table cell the flex wrapper and the raw
|
|
@@ -806,7 +829,20 @@ const InputAreaImpl = InputFrame.styleable<{
|
|
|
806
829
|
// (min-width:auto floors flex items at it); width 100% makes the input
|
|
807
830
|
// adopt the flexed width instead of its size-attribute default. Explicit
|
|
808
831
|
// consumer widths (OTP digit boxes) still win via the later spread.
|
|
809
|
-
<View
|
|
832
|
+
<View
|
|
833
|
+
flex={hugWidth ? 0 : 1}
|
|
834
|
+
{...(hugWidth && typeof widthProp === "number"
|
|
835
|
+
? {
|
|
836
|
+
width: widthProp,
|
|
837
|
+
minWidth: minWidthProp ?? widthProp,
|
|
838
|
+
maxWidth: maxWidthProp ?? widthProp,
|
|
839
|
+
}
|
|
840
|
+
: undefined)}
|
|
841
|
+
{...(heightProp != null
|
|
842
|
+
? { height: heightProp, minHeight: minHeightProp ?? 0, maxHeight: maxHeightProp }
|
|
843
|
+
: undefined)}
|
|
844
|
+
{...(inTableCell ? { minWidth: 0 } : undefined)}
|
|
845
|
+
>
|
|
810
846
|
<InputFrame
|
|
811
847
|
aria-describedby={fieldDescribedBy}
|
|
812
848
|
{...nativeA11yProps}
|
|
@@ -837,7 +873,25 @@ const InputAreaImpl = InputFrame.styleable<{
|
|
|
837
873
|
: undefined
|
|
838
874
|
}
|
|
839
875
|
{...props}
|
|
876
|
+
{...(flexProp != null ? { flex: flexProp } : undefined)}
|
|
877
|
+
{...(widthProp != null
|
|
878
|
+
? { width: widthProp, minWidth: minWidthProp, maxWidth: maxWidthProp }
|
|
879
|
+
: undefined)}
|
|
880
|
+
{...(heightProp != null
|
|
881
|
+
? { height: heightProp, minHeight: minHeightProp, maxHeight: maxHeightProp }
|
|
882
|
+
: undefined)}
|
|
840
883
|
{...webAreaAttrs(classNameProp)}
|
|
884
|
+
{...(isWeb && typeof heightProp === "number"
|
|
885
|
+
? {
|
|
886
|
+
// Beat the UA / `$true` 44px min on <input> when a recipe height is set
|
|
887
|
+
// (Duration size:$3 is 36; a 44 input overflows the box).
|
|
888
|
+
style: {
|
|
889
|
+
height: heightProp,
|
|
890
|
+
minHeight: typeof minHeightProp === "number" ? minHeightProp : 0,
|
|
891
|
+
maxHeight: typeof maxHeightProp === "number" ? maxHeightProp : heightProp,
|
|
892
|
+
},
|
|
893
|
+
}
|
|
894
|
+
: undefined)}
|
|
841
895
|
{...AREA_NO_OUTLINE}
|
|
842
896
|
focusStyle={{ ...focusStyleProp, ...AREA_NO_OUTLINE }}
|
|
843
897
|
focusVisibleStyle={{ ...focusVisibleStyleProp, ...AREA_NO_OUTLINE }}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SpinnerGlyph and useSpinnerStroke (MPO-143).
|
|
3
|
+
*
|
|
4
|
+
* Spinner.spec.tsx covers the status region, the size ladder and the animation
|
|
5
|
+
* knob; the glyph inside it — the two-circle arc that IS the spinner — had no
|
|
6
|
+
* coverage. Two contracts live here: the arc is exactly 270 degrees of the
|
|
7
|
+
* circumference on a faint full track, and `useSpinnerStroke` resolves a `$`
|
|
8
|
+
* token through the theme while passing a literal colour straight through.
|
|
9
|
+
*/
|
|
10
|
+
import { cleanup } from "@testing-library/react";
|
|
11
|
+
import { renderWithProviders } from "@multiplatform.one/test-utils";
|
|
12
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
13
|
+
import { SpinnerGlyph, useSpinnerStroke } from "./SpinnerGlyph";
|
|
14
|
+
|
|
15
|
+
afterEach(cleanup);
|
|
16
|
+
|
|
17
|
+
const R = 9;
|
|
18
|
+
const CIRCUMFERENCE = 2 * Math.PI * R;
|
|
19
|
+
|
|
20
|
+
function svg(container: HTMLElement): SVGElement {
|
|
21
|
+
const node = container.querySelector("svg");
|
|
22
|
+
if (!node) throw new Error("SpinnerGlyph rendered no svg");
|
|
23
|
+
return node as unknown as SVGElement;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function circles(container: HTMLElement): SVGElement[] {
|
|
27
|
+
return Array.from(container.querySelectorAll("circle")) as unknown as SVGElement[];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function Probe({ color }: { color?: string }) {
|
|
31
|
+
const stroke = useSpinnerStroke(color);
|
|
32
|
+
return <span data-stroke={stroke} />;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function stroke(container: HTMLElement): string | null {
|
|
36
|
+
return container.querySelector("[data-stroke]")?.getAttribute("data-stroke") ?? null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
describe("SpinnerGlyph — the arc", () => {
|
|
40
|
+
it("draws a track and a head, never one circle", () => {
|
|
41
|
+
const { container } = renderWithProviders(<SpinnerGlyph color="#123456" size={24} />);
|
|
42
|
+
expect(circles(container)).toHaveLength(2);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("keeps the viewBox fixed and scales through width/height, so it stays crisp", () => {
|
|
46
|
+
const { container } = renderWithProviders(<SpinnerGlyph color="#123456" size={48} />);
|
|
47
|
+
const node = svg(container);
|
|
48
|
+
expect(node.getAttribute("viewBox")).toBe("0 0 24 24");
|
|
49
|
+
expect(node.getAttribute("width")).toBe("48");
|
|
50
|
+
expect(node.getAttribute("height")).toBe("48");
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("takes the box size it is handed rather than a fixed one", () => {
|
|
54
|
+
const small = renderWithProviders(<SpinnerGlyph color="#123456" size={16} />);
|
|
55
|
+
expect(svg(small.container).getAttribute("width")).toBe("16");
|
|
56
|
+
cleanup();
|
|
57
|
+
const large = renderWithProviders(<SpinnerGlyph color="#123456" size={64} />);
|
|
58
|
+
expect(svg(large.container).getAttribute("width")).toBe("64");
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it("makes the track faint and the head solid, both in the requested colour", () => {
|
|
62
|
+
const { container } = renderWithProviders(<SpinnerGlyph color="#abcdef" size={24} />);
|
|
63
|
+
const [track, head] = circles(container);
|
|
64
|
+
expect(track.getAttribute("stroke")).toBe("#abcdef");
|
|
65
|
+
expect(head.getAttribute("stroke")).toBe("#abcdef");
|
|
66
|
+
expect(Number(track.getAttribute("opacity"))).toBeCloseTo(0.2, 5);
|
|
67
|
+
expect(head.getAttribute("opacity")).toBeNull();
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("paints 270 degrees of arc and leaves the remaining quarter open", () => {
|
|
71
|
+
const { container } = renderWithProviders(<SpinnerGlyph color="#123456" size={24} />);
|
|
72
|
+
const [, head] = circles(container);
|
|
73
|
+
const [drawn, gap] = String(
|
|
74
|
+
head.getAttribute("stroke-dasharray") ?? head.getAttribute("strokeDasharray"),
|
|
75
|
+
)
|
|
76
|
+
.split(" ")
|
|
77
|
+
.map(Number);
|
|
78
|
+
expect(drawn).toBeCloseTo(CIRCUMFERENCE * 0.75, 5);
|
|
79
|
+
expect(gap).toBeCloseTo(CIRCUMFERENCE * 0.25, 5);
|
|
80
|
+
expect(drawn + gap).toBeCloseTo(CIRCUMFERENCE, 5);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("starts the head at twelve o'clock, not at three", () => {
|
|
84
|
+
const { container } = renderWithProviders(<SpinnerGlyph color="#123456" size={24} />);
|
|
85
|
+
const [, head] = circles(container);
|
|
86
|
+
expect(head.getAttribute("transform")).toBe("rotate(-90 12 12)");
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("rounds the head's cap, which is what makes it read as Linear/Polaris rather than a pie slice", () => {
|
|
90
|
+
const { container } = renderWithProviders(<SpinnerGlyph color="#123456" size={24} />);
|
|
91
|
+
const [track, head] = circles(container);
|
|
92
|
+
expect(head.getAttribute("stroke-linecap") ?? head.getAttribute("strokeLinecap")).toBe("round");
|
|
93
|
+
expect(track.getAttribute("stroke-linecap") ?? track.getAttribute("strokeLinecap")).toBeNull();
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it("fills neither circle, so the spinner is a ring and not a disc", () => {
|
|
97
|
+
const { container } = renderWithProviders(<SpinnerGlyph color="#123456" size={24} />);
|
|
98
|
+
for (const circle of circles(container)) {
|
|
99
|
+
expect(circle.getAttribute("fill")).toBe("none");
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
describe("useSpinnerStroke", () => {
|
|
105
|
+
it("passes a literal colour through untouched", () => {
|
|
106
|
+
const { container } = renderWithProviders(<Probe color="#ff0000" />);
|
|
107
|
+
expect(stroke(container)).toBe("#ff0000");
|
|
108
|
+
cleanup();
|
|
109
|
+
const named = renderWithProviders(<Probe color="rebeccapurple" />);
|
|
110
|
+
expect(stroke(named.container)).toBe("rebeccapurple");
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it("resolves a $ token to a concrete colour instead of leaking the token", () => {
|
|
114
|
+
const { container } = renderWithProviders(<Probe color="$color" />);
|
|
115
|
+
const value = stroke(container);
|
|
116
|
+
expect(value).toBeTruthy();
|
|
117
|
+
expect(value?.startsWith("$")).toBe(false);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it("defaults to $color when no colour is given, and still resolves it", () => {
|
|
121
|
+
const explicit = renderWithProviders(<Probe color="$color" />);
|
|
122
|
+
const explicitValue = stroke(explicit.container);
|
|
123
|
+
cleanup();
|
|
124
|
+
const implicit = renderWithProviders(<Probe />);
|
|
125
|
+
expect(stroke(implicit.container)).toBe(explicitValue);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it("hands back the token itself when the theme has no such key, rather than throwing", () => {
|
|
129
|
+
const { container } = renderWithProviders(<Probe color="$definitelyNotAThemeKey" />);
|
|
130
|
+
expect(stroke(container)).toBe("$definitelyNotAThemeKey");
|
|
131
|
+
});
|
|
132
|
+
});
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WheelEdgeFade — LC-25's two halves (MPO-143).
|
|
3
|
+
*
|
|
4
|
+
* The contract is a platform split with a contrast reason behind it. On web the
|
|
5
|
+
* wheel viewport carries a CSS mask, so this component must render NOTHING; on
|
|
6
|
+
* native it paints two absolutely-positioned gradient bands as an OVERLAY. The
|
|
7
|
+
* overlay is the whole point: fading the rows themselves would drop their
|
|
8
|
+
* opacity below 1 and take the inactive `$color11` numerals under the AA floor,
|
|
9
|
+
* which is the regression Wheel.spec.tsx guards from the other side.
|
|
10
|
+
*
|
|
11
|
+
* Both halves are exercised. A spec that covered only the file the web build
|
|
12
|
+
* resolves would be asserting `null` and calling it a test.
|
|
13
|
+
*
|
|
14
|
+
* `expo-linear-gradient` ships untranspiled JSX in its published build, which
|
|
15
|
+
* the web transform cannot parse, so it is mocked into a probe element. The
|
|
16
|
+
* mock is the gradient, not the component under test: colours, start and end
|
|
17
|
+
* come straight from WheelEdgeFade.
|
|
18
|
+
*/
|
|
19
|
+
import { cleanup } from "@testing-library/react";
|
|
20
|
+
import { renderWithProviders } from "@multiplatform.one/test-utils";
|
|
21
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
22
|
+
import { WheelEdgeFade } from "./WheelEdgeFade";
|
|
23
|
+
import { WheelEdgeFade as NativeWheelEdgeFade } from "./WheelEdgeFade.native";
|
|
24
|
+
|
|
25
|
+
vi.mock("expo-linear-gradient", () => ({
|
|
26
|
+
LinearGradient: (props: { colors?: string[]; start?: unknown; end?: unknown }) => (
|
|
27
|
+
<div
|
|
28
|
+
data-gradient="true"
|
|
29
|
+
data-colors={(props.colors ?? []).join("|")}
|
|
30
|
+
data-start={JSON.stringify(props.start)}
|
|
31
|
+
data-end={JSON.stringify(props.end)}
|
|
32
|
+
/>
|
|
33
|
+
),
|
|
34
|
+
}));
|
|
35
|
+
|
|
36
|
+
afterEach(cleanup);
|
|
37
|
+
|
|
38
|
+
function bands(container: HTMLElement): HTMLElement[] {
|
|
39
|
+
return Array.from(container.querySelectorAll("[style*='height']")) as HTMLElement[];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function gradients(container: HTMLElement): HTMLElement[] {
|
|
43
|
+
return Array.from(container.querySelectorAll("[data-gradient]")) as HTMLElement[];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function colorsOf(node: HTMLElement): string[] {
|
|
47
|
+
return String(node.getAttribute("data-colors") ?? "").split("|");
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
describe("WheelEdgeFade — web", () => {
|
|
51
|
+
it("renders nothing, because the viewport's CSS mask already does the fade", () => {
|
|
52
|
+
const { container } = renderWithProviders(<WheelEdgeFade height={24} />);
|
|
53
|
+
expect(container.querySelector("[data-gradient]")).toBeNull();
|
|
54
|
+
expect(container.textContent).toBe("");
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("renders nothing at every height, so it can never dim a row", () => {
|
|
58
|
+
for (const height of [0, 1, 24, 400]) {
|
|
59
|
+
const { container } = renderWithProviders(<WheelEdgeFade height={height} />);
|
|
60
|
+
expect(container.querySelector("[data-gradient]"), String(height)).toBeNull();
|
|
61
|
+
cleanup();
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
describe("WheelEdgeFade — native", () => {
|
|
67
|
+
it("paints one band per edge, never a single one", () => {
|
|
68
|
+
const { container } = renderWithProviders(<NativeWheelEdgeFade height={24} />);
|
|
69
|
+
expect(gradients(container)).toHaveLength(2);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("draws nothing at all when the band height is zero or negative", () => {
|
|
73
|
+
const zero = renderWithProviders(<NativeWheelEdgeFade height={0} />);
|
|
74
|
+
expect(gradients(zero.container)).toHaveLength(0);
|
|
75
|
+
cleanup();
|
|
76
|
+
const negative = renderWithProviders(<NativeWheelEdgeFade height={-8} />);
|
|
77
|
+
expect(gradients(negative.container)).toHaveLength(0);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it("pins one band to the top and one to the bottom", () => {
|
|
81
|
+
const { container } = renderWithProviders(<NativeWheelEdgeFade height={30} />);
|
|
82
|
+
const edges = bands(container).map((node) => node.getAttribute("style") ?? "");
|
|
83
|
+
expect(edges.some((style) => /top:\s*0px/.test(style))).toBe(true);
|
|
84
|
+
expect(edges.some((style) => /bottom:\s*0px/.test(style))).toBe(true);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it("gives each band the height it was handed, so the caller sets the fade depth", () => {
|
|
88
|
+
const { container } = renderWithProviders(<NativeWheelEdgeFade height={30} />);
|
|
89
|
+
const heights = bands(container).map(
|
|
90
|
+
(node) => /height:\s*(\d+)px/.exec(node.getAttribute("style") ?? "")?.[1],
|
|
91
|
+
);
|
|
92
|
+
expect(heights.filter(Boolean)).toEqual(["30", "30"]);
|
|
93
|
+
cleanup();
|
|
94
|
+
const taller = renderWithProviders(<NativeWheelEdgeFade height={64} />);
|
|
95
|
+
expect(
|
|
96
|
+
bands(taller.container).map(
|
|
97
|
+
(node) => /height:\s*(\d+)px/.exec(node.getAttribute("style") ?? "")?.[1],
|
|
98
|
+
),
|
|
99
|
+
).toEqual(["64", "64"]);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it("keeps the bands out of the way of a drag, so the wheel still scrolls", () => {
|
|
103
|
+
const { container } = renderWithProviders(<NativeWheelEdgeFade height={24} />);
|
|
104
|
+
for (const band of bands(container)) {
|
|
105
|
+
expect(band.className).toContain("r-pointerEvents");
|
|
106
|
+
expect(band.className).toContain("r-position");
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it("runs the top band opaque-to-clear and the bottom band clear-to-opaque", () => {
|
|
111
|
+
const { container } = renderWithProviders(<NativeWheelEdgeFade height={24} />);
|
|
112
|
+
const [top, bottom] = gradients(container).map(colorsOf);
|
|
113
|
+
expect(top).toHaveLength(2);
|
|
114
|
+
expect(bottom).toHaveLength(2);
|
|
115
|
+
// The two bands are mirror images: the top starts on the page background
|
|
116
|
+
// and ends transparent, the bottom does the reverse.
|
|
117
|
+
expect(top[0]).toBe(bottom[1]);
|
|
118
|
+
expect(top[1]).toBe(bottom[0]);
|
|
119
|
+
expect(top[1]).toMatch(/,\s*0\)$/);
|
|
120
|
+
expect(top[0]).not.toMatch(/,\s*0\)$/);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it("derives the fade colour from the theme background rather than a hardcoded white", () => {
|
|
124
|
+
const { container } = renderWithProviders(<NativeWheelEdgeFade height={24} />);
|
|
125
|
+
const [opaque, clear] = colorsOf(gradients(container)[0]);
|
|
126
|
+
expect(opaque).toBeTruthy();
|
|
127
|
+
expect(opaque).not.toBe("transparent");
|
|
128
|
+
// The clear stop is the SAME colour at zero alpha, so the band never
|
|
129
|
+
// shifts hue across its length.
|
|
130
|
+
const channels = /rgba\(([^)]+),\s*0\)$/.exec(clear)?.[1];
|
|
131
|
+
expect(channels, `clear stop ${clear} must be the opaque stop at alpha 0`).toBeTruthy();
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it("runs both gradients vertically down the centre line", () => {
|
|
135
|
+
const { container } = renderWithProviders(<NativeWheelEdgeFade height={24} />);
|
|
136
|
+
for (const gradient of gradients(container)) {
|
|
137
|
+
expect(gradient.getAttribute("data-start")).toBe(JSON.stringify({ x: 0.5, y: 0 }));
|
|
138
|
+
expect(gradient.getAttribute("data-end")).toBe(JSON.stringify({ x: 0.5, y: 1 }));
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
});
|