@oxy.so/bloom 3.0.1 → 3.1.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/docs/adoption-matrix.mdx +266 -0
- package/docs/checkbox.mdx +16 -0
- package/docs/composition.mdx +88 -16
- package/docs/date-picker.mdx +11 -0
- package/docs/field.mdx +35 -4
- package/docs/file-upload.mdx +13 -0
- package/docs/input-group.mdx +22 -0
- package/docs/input-otp.mdx +14 -0
- package/docs/layout.mdx +34 -0
- package/docs/migrating-to-3.0.mdx +43 -4
- package/docs/phone-input.mdx +17 -0
- package/docs/radio.mdx +16 -0
- package/docs/rating.mdx +14 -0
- package/docs/segmented-control.mdx +16 -4
- package/docs/select.mdx +22 -0
- package/docs/slider.mdx +14 -0
- package/docs/stepper.mdx +3 -1
- package/docs/switch.mdx +14 -0
- package/docs/text-field.mdx +14 -0
- package/docs/textarea.mdx +20 -0
- package/lib/commonjs/bottom-sheet/BottomSheetBase.js +11 -4
- package/lib/commonjs/bottom-sheet/BottomSheetBase.js.map +1 -1
- package/lib/commonjs/checkbox/Checkbox.js +24 -8
- package/lib/commonjs/checkbox/Checkbox.js.map +1 -1
- package/lib/commonjs/date-picker/TimeField.js +18 -6
- package/lib/commonjs/date-picker/TimeField.js.map +1 -1
- package/lib/commonjs/dialog/DialogContent.js +36 -29
- package/lib/commonjs/dialog/DialogContent.js.map +1 -1
- package/lib/commonjs/field/index.js +7 -0
- package/lib/commonjs/field/index.js.map +1 -1
- package/lib/commonjs/field/membership.js +105 -0
- package/lib/commonjs/field/membership.js.map +1 -0
- package/lib/commonjs/file-upload/FileUpload.js +17 -3
- package/lib/commonjs/file-upload/FileUpload.js.map +1 -1
- package/lib/commonjs/index.js +21 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/index.web.js +21 -0
- package/lib/commonjs/index.web.js.map +1 -1
- package/lib/commonjs/input-group/InputGroup.js +18 -5
- package/lib/commonjs/input-group/InputGroup.js.map +1 -1
- package/lib/commonjs/input-otp/InputOtp.js +20 -8
- package/lib/commonjs/input-otp/InputOtp.js.map +1 -1
- package/lib/commonjs/layout/index.js +7 -0
- package/lib/commonjs/layout/index.js.map +1 -1
- package/lib/commonjs/layout/screen-scope.js +61 -0
- package/lib/commonjs/layout/screen-scope.js.map +1 -0
- package/lib/commonjs/phone-input/PhoneInput.js +20 -8
- package/lib/commonjs/phone-input/PhoneInput.js.map +1 -1
- package/lib/commonjs/radio/Radio.js +35 -9
- package/lib/commonjs/radio/Radio.js.map +1 -1
- package/lib/commonjs/rating/RatingInput.js +14 -6
- package/lib/commonjs/rating/RatingInput.js.map +1 -1
- package/lib/commonjs/segmented-control/SegmentedControl.js +38 -8
- package/lib/commonjs/segmented-control/SegmentedControl.js.map +1 -1
- package/lib/commonjs/select/Select.js +15 -3
- package/lib/commonjs/select/Select.js.map +1 -1
- package/lib/commonjs/select/Select.web.js +13 -5
- package/lib/commonjs/select/Select.web.js.map +1 -1
- package/lib/commonjs/slider/Slider.js +17 -3
- package/lib/commonjs/slider/Slider.js.map +1 -1
- package/lib/commonjs/stepper/Stepper.js +16 -7
- package/lib/commonjs/stepper/Stepper.js.map +1 -1
- package/lib/commonjs/styles/styled-primitives.js.map +1 -1
- package/lib/commonjs/switch/Switch.js +23 -8
- package/lib/commonjs/switch/Switch.js.map +1 -1
- package/lib/commonjs/text-field/TextField.js +19 -4
- package/lib/commonjs/text-field/TextField.js.map +1 -1
- package/lib/commonjs/textarea/Textarea.js +39 -15
- package/lib/commonjs/textarea/Textarea.js.map +1 -1
- package/lib/module/bottom-sheet/BottomSheetBase.js +8 -1
- package/lib/module/bottom-sheet/BottomSheetBase.js.map +1 -1
- package/lib/module/checkbox/Checkbox.js +24 -8
- package/lib/module/checkbox/Checkbox.js.map +1 -1
- package/lib/module/date-picker/TimeField.js +19 -5
- package/lib/module/date-picker/TimeField.js.map +1 -1
- package/lib/module/dialog/DialogContent.js +37 -30
- package/lib/module/dialog/DialogContent.js.map +1 -1
- package/lib/module/field/index.js +1 -0
- package/lib/module/field/index.js.map +1 -1
- package/lib/module/field/membership.js +101 -0
- package/lib/module/field/membership.js.map +1 -0
- package/lib/module/file-upload/FileUpload.js +17 -3
- package/lib/module/file-upload/FileUpload.js.map +1 -1
- package/lib/module/index.js +2 -2
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.web.js +2 -2
- package/lib/module/index.web.js.map +1 -1
- package/lib/module/input-group/InputGroup.js +19 -4
- package/lib/module/input-group/InputGroup.js.map +1 -1
- package/lib/module/input-otp/InputOtp.js +21 -7
- package/lib/module/input-otp/InputOtp.js.map +1 -1
- package/lib/module/layout/index.js +4 -1
- package/lib/module/layout/index.js.map +1 -1
- package/lib/module/layout/screen-scope.js +57 -0
- package/lib/module/layout/screen-scope.js.map +1 -0
- package/lib/module/phone-input/PhoneInput.js +22 -9
- package/lib/module/phone-input/PhoneInput.js.map +1 -1
- package/lib/module/radio/Radio.js +35 -9
- package/lib/module/radio/Radio.js.map +1 -1
- package/lib/module/rating/RatingInput.js +15 -5
- package/lib/module/rating/RatingInput.js.map +1 -1
- package/lib/module/segmented-control/SegmentedControl.js +38 -8
- package/lib/module/segmented-control/SegmentedControl.js.map +1 -1
- package/lib/module/select/Select.js +15 -3
- package/lib/module/select/Select.js.map +1 -1
- package/lib/module/select/Select.web.js +14 -4
- package/lib/module/select/Select.web.js.map +1 -1
- package/lib/module/slider/Slider.js +17 -3
- package/lib/module/slider/Slider.js.map +1 -1
- package/lib/module/stepper/Stepper.js +17 -6
- package/lib/module/stepper/Stepper.js.map +1 -1
- package/lib/module/styles/styled-primitives.js.map +1 -1
- package/lib/module/switch/Switch.js +23 -8
- package/lib/module/switch/Switch.js.map +1 -1
- package/lib/module/text-field/TextField.js +19 -4
- package/lib/module/text-field/TextField.js.map +1 -1
- package/lib/module/textarea/Textarea.js +39 -15
- package/lib/module/textarea/Textarea.js.map +1 -1
- package/lib/typescript/commonjs/bottom-sheet/BottomSheetBase.d.ts.map +1 -1
- package/lib/typescript/commonjs/checkbox/Checkbox.d.ts.map +1 -1
- package/lib/typescript/commonjs/checkbox/types.d.ts +5 -0
- package/lib/typescript/commonjs/checkbox/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/date-picker/TimeField.d.ts +1 -1
- package/lib/typescript/commonjs/date-picker/TimeField.d.ts.map +1 -1
- package/lib/typescript/commonjs/date-picker/types.d.ts +6 -3
- package/lib/typescript/commonjs/date-picker/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/dialog/DialogContent.d.ts.map +1 -1
- package/lib/typescript/commonjs/field/index.d.ts +2 -0
- package/lib/typescript/commonjs/field/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/field/membership.d.ts +60 -0
- package/lib/typescript/commonjs/field/membership.d.ts.map +1 -0
- package/lib/typescript/commonjs/file-upload/FileUpload.d.ts +1 -1
- package/lib/typescript/commonjs/file-upload/FileUpload.d.ts.map +1 -1
- package/lib/typescript/commonjs/floating/types.d.ts +13 -0
- package/lib/typescript/commonjs/floating/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +3 -3
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.web.d.ts +3 -3
- package/lib/typescript/commonjs/index.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/input-group/InputGroup.d.ts +1 -1
- package/lib/typescript/commonjs/input-group/InputGroup.d.ts.map +1 -1
- package/lib/typescript/commonjs/input-otp/InputOtp.d.ts.map +1 -1
- package/lib/typescript/commonjs/input-otp/types.d.ts +7 -1
- package/lib/typescript/commonjs/input-otp/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/layout/index.d.ts +4 -1
- package/lib/typescript/commonjs/layout/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/layout/screen-scope.d.ts +40 -0
- package/lib/typescript/commonjs/layout/screen-scope.d.ts.map +1 -0
- package/lib/typescript/commonjs/phone-input/PhoneInput.d.ts.map +1 -1
- package/lib/typescript/commonjs/radio/Radio.d.ts +1 -1
- package/lib/typescript/commonjs/radio/Radio.d.ts.map +1 -1
- package/lib/typescript/commonjs/radio/types.d.ts +11 -3
- package/lib/typescript/commonjs/radio/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/rating/RatingInput.d.ts +1 -1
- package/lib/typescript/commonjs/rating/RatingInput.d.ts.map +1 -1
- package/lib/typescript/commonjs/rating/types.d.ts +6 -3
- package/lib/typescript/commonjs/rating/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/segmented-control/SegmentedControl.d.ts +18 -2
- package/lib/typescript/commonjs/segmented-control/SegmentedControl.d.ts.map +1 -1
- package/lib/typescript/commonjs/select/Select.d.ts.map +1 -1
- package/lib/typescript/commonjs/select/Select.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/select/types.d.ts +8 -1
- package/lib/typescript/commonjs/select/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/slider/Slider.d.ts.map +1 -1
- package/lib/typescript/commonjs/stepper/Stepper.d.ts +1 -1
- package/lib/typescript/commonjs/stepper/Stepper.d.ts.map +1 -1
- package/lib/typescript/commonjs/stepper/types.d.ts +8 -1
- package/lib/typescript/commonjs/stepper/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/styles/styled-primitives.d.ts +16 -0
- package/lib/typescript/commonjs/styles/styled-primitives.d.ts.map +1 -1
- package/lib/typescript/commonjs/switch/Switch.d.ts.map +1 -1
- package/lib/typescript/commonjs/switch/types.d.ts +7 -0
- package/lib/typescript/commonjs/switch/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/text-field/TextField.d.ts.map +1 -1
- package/lib/typescript/commonjs/textarea/Textarea.d.ts +1 -1
- package/lib/typescript/commonjs/textarea/Textarea.d.ts.map +1 -1
- package/lib/typescript/module/bottom-sheet/BottomSheetBase.d.ts.map +1 -1
- package/lib/typescript/module/checkbox/Checkbox.d.ts.map +1 -1
- package/lib/typescript/module/checkbox/types.d.ts +5 -0
- package/lib/typescript/module/checkbox/types.d.ts.map +1 -1
- package/lib/typescript/module/date-picker/TimeField.d.ts +1 -1
- package/lib/typescript/module/date-picker/TimeField.d.ts.map +1 -1
- package/lib/typescript/module/date-picker/types.d.ts +6 -3
- package/lib/typescript/module/date-picker/types.d.ts.map +1 -1
- package/lib/typescript/module/dialog/DialogContent.d.ts.map +1 -1
- package/lib/typescript/module/field/index.d.ts +2 -0
- package/lib/typescript/module/field/index.d.ts.map +1 -1
- package/lib/typescript/module/field/membership.d.ts +60 -0
- package/lib/typescript/module/field/membership.d.ts.map +1 -0
- package/lib/typescript/module/file-upload/FileUpload.d.ts +1 -1
- package/lib/typescript/module/file-upload/FileUpload.d.ts.map +1 -1
- package/lib/typescript/module/floating/types.d.ts +13 -0
- package/lib/typescript/module/floating/types.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +3 -3
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/index.web.d.ts +3 -3
- package/lib/typescript/module/index.web.d.ts.map +1 -1
- package/lib/typescript/module/input-group/InputGroup.d.ts +1 -1
- package/lib/typescript/module/input-group/InputGroup.d.ts.map +1 -1
- package/lib/typescript/module/input-otp/InputOtp.d.ts.map +1 -1
- package/lib/typescript/module/input-otp/types.d.ts +7 -1
- package/lib/typescript/module/input-otp/types.d.ts.map +1 -1
- package/lib/typescript/module/layout/index.d.ts +4 -1
- package/lib/typescript/module/layout/index.d.ts.map +1 -1
- package/lib/typescript/module/layout/screen-scope.d.ts +40 -0
- package/lib/typescript/module/layout/screen-scope.d.ts.map +1 -0
- package/lib/typescript/module/phone-input/PhoneInput.d.ts.map +1 -1
- package/lib/typescript/module/radio/Radio.d.ts +1 -1
- package/lib/typescript/module/radio/Radio.d.ts.map +1 -1
- package/lib/typescript/module/radio/types.d.ts +11 -3
- package/lib/typescript/module/radio/types.d.ts.map +1 -1
- package/lib/typescript/module/rating/RatingInput.d.ts +1 -1
- package/lib/typescript/module/rating/RatingInput.d.ts.map +1 -1
- package/lib/typescript/module/rating/types.d.ts +6 -3
- package/lib/typescript/module/rating/types.d.ts.map +1 -1
- package/lib/typescript/module/segmented-control/SegmentedControl.d.ts +18 -2
- package/lib/typescript/module/segmented-control/SegmentedControl.d.ts.map +1 -1
- package/lib/typescript/module/select/Select.d.ts.map +1 -1
- package/lib/typescript/module/select/Select.web.d.ts.map +1 -1
- package/lib/typescript/module/select/types.d.ts +8 -1
- package/lib/typescript/module/select/types.d.ts.map +1 -1
- package/lib/typescript/module/slider/Slider.d.ts.map +1 -1
- package/lib/typescript/module/stepper/Stepper.d.ts +1 -1
- package/lib/typescript/module/stepper/Stepper.d.ts.map +1 -1
- package/lib/typescript/module/stepper/types.d.ts +8 -1
- package/lib/typescript/module/stepper/types.d.ts.map +1 -1
- package/lib/typescript/module/styles/styled-primitives.d.ts +16 -0
- package/lib/typescript/module/styles/styled-primitives.d.ts.map +1 -1
- package/lib/typescript/module/switch/Switch.d.ts.map +1 -1
- package/lib/typescript/module/switch/types.d.ts +7 -0
- package/lib/typescript/module/switch/types.d.ts.map +1 -1
- package/lib/typescript/module/text-field/TextField.d.ts.map +1 -1
- package/lib/typescript/module/textarea/Textarea.d.ts +1 -1
- package/lib/typescript/module/textarea/Textarea.d.ts.map +1 -1
- package/package.json +2 -1
- package/src/bottom-sheet/BottomSheetBase.tsx +6 -1
- package/src/checkbox/Checkbox.tsx +24 -8
- package/src/checkbox/types.ts +5 -0
- package/src/date-picker/TimeField.tsx +14 -3
- package/src/date-picker/types.ts +6 -3
- package/src/dialog/DialogContent.tsx +6 -2
- package/src/field/index.ts +2 -0
- package/src/field/membership.ts +164 -0
- package/src/file-upload/FileUpload.tsx +14 -3
- package/src/floating/types.ts +13 -0
- package/src/index.ts +9 -2
- package/src/index.web.ts +9 -2
- package/src/input-group/InputGroup.tsx +13 -2
- package/src/input-otp/InputOtp.tsx +19 -5
- package/src/input-otp/types.ts +7 -1
- package/src/layout/index.ts +4 -1
- package/src/layout/screen-scope.tsx +54 -0
- package/src/phone-input/PhoneInput.tsx +20 -8
- package/src/radio/Radio.tsx +32 -9
- package/src/radio/types.ts +11 -3
- package/src/rating/RatingInput.tsx +10 -3
- package/src/rating/types.ts +6 -3
- package/src/segmented-control/SegmentedControl.tsx +51 -8
- package/src/select/Select.tsx +14 -2
- package/src/select/Select.web.tsx +11 -2
- package/src/select/types.ts +8 -1
- package/src/slider/Slider.tsx +13 -3
- package/src/stepper/Stepper.tsx +12 -4
- package/src/stepper/types.ts +8 -1
- package/src/styles/styled-primitives.ts +16 -0
- package/src/switch/Switch.tsx +22 -9
- package/src/switch/types.ts +7 -0
- package/src/text-field/TextField.tsx +16 -4
- package/src/textarea/Textarea.tsx +38 -15
|
@@ -59,6 +59,22 @@ export interface WebAriaProps {
|
|
|
59
59
|
* currently needs; the union is the ARIA one, narrowed to what is spellable.
|
|
60
60
|
*/
|
|
61
61
|
'aria-haspopup'?: 'menu' | 'listbox' | 'tree' | 'grid' | 'dialog';
|
|
62
|
+
/**
|
|
63
|
+
* The id list of the elements that DESCRIBE this control — a field's hint or
|
|
64
|
+
* its error message (`field/context.ts`).
|
|
65
|
+
*
|
|
66
|
+
* Here for the same reason as `aria-haspopup`: React Native types no such prop
|
|
67
|
+
* (there is no `accessibilityState` entry for it to fold into) while
|
|
68
|
+
* react-native-web forwards it, so web gets the association and native is
|
|
69
|
+
* unaffected. A control that needs the description READ on native carries it
|
|
70
|
+
* in its accessible name instead.
|
|
71
|
+
*/
|
|
72
|
+
'aria-describedby'?: string;
|
|
73
|
+
/**
|
|
74
|
+
* Whether the control's current value is invalid. Same platform split as
|
|
75
|
+
* `aria-describedby`.
|
|
76
|
+
*/
|
|
77
|
+
'aria-invalid'?: boolean;
|
|
62
78
|
}
|
|
63
79
|
/**
|
|
64
80
|
* `ref` is on the PUBLISHED type and deliberately not on the one handed to
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"styled-primitives.d.ts","sourceRoot":"","sources":["../../../../src/styles/styled-primitives.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AAChD,OAAO,EACL,KAAK,EAEL,IAAI,EACJ,IAAI,EACJ,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,KAAK,SAAS,EACd,KAAK,SAAS,EACd,KAAK,SAAS,EACd,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AAGtB;;;;;;GAMG;AACH,KAAK,mBAAmB,GAAG,IAAI,CAAC,cAAc,EAAE,OAAO,CAAC,GAAG;IACzD,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B,CAAC;AAEF;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,WAAW,YAAY;IAC3B;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"styled-primitives.d.ts","sourceRoot":"","sources":["../../../../src/styles/styled-primitives.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AAChD,OAAO,EACL,KAAK,EAEL,IAAI,EACJ,IAAI,EACJ,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,KAAK,SAAS,EACd,KAAK,SAAS,EACd,KAAK,SAAS,EACd,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AAGtB;;;;;;GAMG;AACH,KAAK,mBAAmB,GAAG,IAAI,CAAC,cAAc,EAAE,OAAO,CAAC,GAAG;IACzD,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B,CAAC;AAEF;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,WAAW,YAAY;IAC3B;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,QAAQ,CAAC;IAClE;;;;;;;;;OASG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED;;;;;GAKG;AACH,MAAM,MAAM,oBAAoB,GAAG,mBAAmB,GACpD,YAAY,GAAG;IACb,GAAG,CAAC,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;CACjB,CAAC;AAEJ;;;;;;GAMG;AACH,eAAO,MAAM,UAAU,EAAE,aAAa,CAAC,SAAS,GAAG;IAAE,GAAG,CAAC,EAAE,GAAG,CAAC,IAAI,CAAC,CAAA;CAAE,CAEpE,CAAC;AAEH,eAAO,MAAM,UAAU,EAAE,aAAa,CAAC,SAAS,GAAG;IAAE,GAAG,CAAC,EAAE,GAAG,CAAC,IAAI,CAAC,CAAA;CAAE,CAEpE,CAAC;AAEH,eAAO,MAAM,WAAW,EAAE,aAAa,CAAC,UAAU,GAAG;IAAE,GAAG,CAAC,EAAE,GAAG,CAAC,KAAK,CAAC,CAAA;CAAE,CAEvE,CAAC;AAEH,eAAO,MAAM,eAAe,EAAE,aAAa,CAAC,oBAAoB,CAG/D,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Switch.d.ts","sourceRoot":"","sources":["../../../../src/switch/Switch.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAoC,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"Switch.d.ts","sourceRoot":"","sources":["../../../../src/switch/Switch.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAoC,MAAM,OAAO,CAAC;AAQzD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AA8I3C,eAAO,MAAM,MAAM,4FAAwB,CAAC"}
|
|
@@ -25,8 +25,15 @@ export interface SwitchProps {
|
|
|
25
25
|
* are not the same rule.)
|
|
26
26
|
*
|
|
27
27
|
* Omitting it warns once in development; see `use-accessible-name-warning`.
|
|
28
|
+
* Inside a `Field` the field's label supplies the name, so a switch in a
|
|
29
|
+
* labelled field needs nothing here and does not warn.
|
|
28
30
|
*/
|
|
29
31
|
accessibilityLabel?: string;
|
|
32
|
+
/**
|
|
33
|
+
* The switch's id. Inside a `Field` the field supplies one, so the label's
|
|
34
|
+
* `htmlFor` points at the control on web.
|
|
35
|
+
*/
|
|
36
|
+
nativeID?: string;
|
|
30
37
|
testID?: string;
|
|
31
38
|
}
|
|
32
39
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/switch/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzD,MAAM,WAAW,WAAW;IAC1B,2BAA2B;IAC3B,KAAK,EAAE,OAAO,CAAC;IACf,8CAA8C;IAC9C,aAAa,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACxC,qCAAqC;IACrC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,sBAAsB;IACtB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,mBAAmB;IACnB,IAAI,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC;IACxB
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/switch/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzD,MAAM,WAAW,WAAW;IAC1B,2BAA2B;IAC3B,KAAK,EAAE,OAAO,CAAC;IACf,8CAA8C;IAC9C,aAAa,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACxC,qCAAqC;IACrC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,sBAAsB;IACtB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,mBAAmB;IACnB,IAAI,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC;IACxB;;;;;;;;;;;;;;;;;OAiBG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TextField.d.ts","sourceRoot":"","sources":["../../../../src/text-field/TextField.tsx"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,kBAAkB,EAQxB,MAAM,cAAc,CAAC;AAOtB,OAAO,EAML,KAAK,aAAa,EACnB,MAAM,WAAW,CAAC;AAGnB,OAAO,EAmBL,KAAK,aAAa,EACnB,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EACV,kBAAkB,EAClB,kBAAkB,EAClB,mBAAmB,EACnB,mBAAmB,EACnB,cAAc,EACf,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"TextField.d.ts","sourceRoot":"","sources":["../../../../src/text-field/TextField.tsx"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,kBAAkB,EAQxB,MAAM,cAAc,CAAC;AAOtB,OAAO,EAML,KAAK,aAAa,EACnB,MAAM,WAAW,CAAC;AAGnB,OAAO,EAmBL,KAAK,aAAa,EACnB,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EACV,kBAAkB,EAClB,kBAAkB,EAClB,mBAAmB,EACnB,mBAAmB,EACnB,cAAc,EACf,MAAM,SAAS,CAAC;AAoCjB;;;;;;;;GAQG;AACH,MAAM,WAAW,0BAA0B;IACzC,IAAI,EAAE,aAAa,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;CACnB;AACD,eAAO,MAAM,qBAAqB,4DAAyD,CAAC;AAiD5F,wBAAgB,SAAS,CAAC,EACxB,QAAQ,EACR,SAAS,EAAE,aAAqB,EAChC,QAAQ,EAAE,YAAoB,EAC9B,IAAI,EAAE,QAAQ,EACd,MAA0B,EAC1B,YAAY,EACZ,KAAK,GACN,EAAE,cAAc,2CAuFhB;AAsFD,wBAAgB,cAAc,CAAC,EAC7B,KAAK,EACL,WAAW,EACX,KAAK,EACL,YAAY,EACZ,OAAO,EACP,MAAM,EACN,SAAS,EACT,QAAQ,EACR,IAAI,EACJ,QAAQ,EACR,KAAK,EACL,aAAqB,EACrB,GAAG,IAAI,EACR,EAAE,mBAAmB,2CA8LrB;AAmKD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,EAC7B,QAAQ,EACR,QAAQ,EACR,QAAgB,EAChB,OAAe,EACf,KAAK,GACN,EAAE,mBAAmB,2CA6BrB;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,EAC5B,QAAQ,EACR,SAAiB,EACjB,QAAQ,EACR,KAAK,GACN,EAAE,kBAAkB,2CAkBpB;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,QAAoB,EAAE,EAAE,kBAAkB,2CA4BrF;AAED,wBAAgB,eAAe,CAAC,EAC9B,QAAQ,EACR,KAAK,EACL,iBAAiB,EACjB,KAAK,GACN,EAAE,KAAK,CAAC,iBAAiB,CACxB,aAAa,GAAG;IACd,KAAK,EAAE,MAAM,CAAC;IACd,iBAAiB,CAAC,EAAE,kBAAkB,CAAC,mBAAmB,CAAC,CAAC;CAC7D,CACF,2CAyBA;AAED,wBAAgB,cAAc,CAAC,EAC7B,QAAQ,EACR,KAAK,GACN,EAAE;IACD,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf,2CAqCA"}
|
|
@@ -16,5 +16,5 @@ import type { TextareaProps } from './types';
|
|
|
16
16
|
* `maxHeight`; on web the same is `field-sizing: content`, which shrinks back
|
|
17
17
|
* as well as growing (browsers without it keep the `rows` height and scroll).
|
|
18
18
|
*/
|
|
19
|
-
export declare function Textarea({ label, hint, placeholder, value, defaultValue, onChangeText, onFocus, onBlur, size, rows, autoResize, maxRows, resize, maxLength, showCount, isInvalid, disabled, required, tooltip, style, inputStyle, inputRef, accessibilityLabel, testID, ...rest }: TextareaProps): import("react/jsx-runtime").JSX.Element;
|
|
19
|
+
export declare function Textarea({ label, hint, placeholder, value, defaultValue, onChangeText, onFocus, onBlur, size: sizeProp, rows, autoResize, maxRows, resize, maxLength, showCount, isInvalid, disabled, required, tooltip, style, inputStyle, inputRef, accessibilityLabel, nativeID, testID, ...rest }: TextareaProps): import("react/jsx-runtime").JSX.Element;
|
|
20
20
|
//# sourceMappingURL=Textarea.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Textarea.d.ts","sourceRoot":"","sources":["../../../../src/textarea/Textarea.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Textarea.d.ts","sourceRoot":"","sources":["../../../../src/textarea/Textarea.tsx"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAO7C;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,QAAQ,CAAC,EACvB,KAAK,EACL,IAAI,EACJ,WAAW,EACX,KAAK,EACL,YAAY,EACZ,YAAY,EACZ,OAAO,EACP,MAAM,EACN,IAAI,EAAE,QAAQ,EACd,IAAQ,EACR,UAAkB,EAClB,OAAO,EACP,MAAmB,EACnB,SAAS,EACT,SAAiB,EACjB,SAAiB,EACjB,QAAgB,EAChB,QAAgB,EAChB,OAAe,EACf,KAAK,EACL,UAAU,EACV,QAAQ,EACR,kBAAkB,EAClB,QAAQ,EACR,MAAM,EACN,GAAG,IAAI,EACR,EAAE,aAAa,2CAkKf"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oxy.so/bloom",
|
|
3
|
-
"version": "3.0
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"packageManager": "bun@1.3.14",
|
|
5
5
|
"description": "Bloom UI — Oxy ecosystem component library for React Native + Expo + Web",
|
|
6
6
|
"main": "lib/commonjs/index.js",
|
|
@@ -2623,6 +2623,7 @@
|
|
|
2623
2623
|
"generate:theme-css": "bun scripts/generate-theme-css.ts",
|
|
2624
2624
|
"generate:theme-golden": "bun scripts/generate-theme-golden.ts",
|
|
2625
2625
|
"generate:tokens-json": "bun scripts/generate-design-tokens-json.ts",
|
|
2626
|
+
"generate:adoption-matrix": "bun scripts/generate-adoption-matrix.ts",
|
|
2626
2627
|
"prebuild": "node scripts/generate-platform-exports.mjs && bun scripts/generate-theme-css.ts && bun scripts/generate-design-tokens-json.ts",
|
|
2627
2628
|
"build": "bob build",
|
|
2628
2629
|
"postbuild": "node scripts/verify-package.mjs && node scripts/verify-build-freshness.mjs --worktree",
|
|
@@ -14,6 +14,7 @@ import { Gesture, GestureDetector, type GestureType } from 'react-native-gesture
|
|
|
14
14
|
import { adoptStyleSheet, dropStyleSheet } from '../styles/adopt-style-sheet';
|
|
15
15
|
import type { WebCssStyle } from '../styles/web-view-style';
|
|
16
16
|
import { SurfaceLevelProvider, surfaceFillVars } from '../styles/surface-levels';
|
|
17
|
+
import { ScreenScope } from '../layout';
|
|
17
18
|
import { Z_INDEX } from '../styles/z-index';
|
|
18
19
|
import Animated, {
|
|
19
20
|
type AnimatedStyle,
|
|
@@ -650,9 +651,13 @@ export const BottomSheetBase = forwardRef((props: BottomSheetBaseProps, ref: Rea
|
|
|
650
651
|
// — web keeps React context across a portal — would tell its content it is
|
|
651
652
|
// sitting on the panel's card, and in-sheet chrome would paint the column's
|
|
652
653
|
// colour on top of the sheet's.
|
|
654
|
+
// ...and it is its own SCREEN for the same reason: a header inside the sheet
|
|
655
|
+
// must claim the sheet's top edge rather than the page's, and chrome inside
|
|
656
|
+
// it must follow the sheet's scroller rather than `window.scrollY`
|
|
657
|
+
// (`layout/screen-scope.tsx`).
|
|
653
658
|
const surfaceChildren = (
|
|
654
659
|
<SurfaceLevelProvider level={0} fill={colors.background}>
|
|
655
|
-
{children}
|
|
660
|
+
<ScreenScope>{children}</ScreenScope>
|
|
656
661
|
</SurfaceLevelProvider>
|
|
657
662
|
);
|
|
658
663
|
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
checkboxLabelLineHeight,
|
|
15
15
|
resolveCheckboxPaint,
|
|
16
16
|
} from './shared';
|
|
17
|
+
import { useFieldMembership } from '../field/membership';
|
|
17
18
|
import type { CheckboxProps } from './types';
|
|
18
19
|
|
|
19
20
|
/**
|
|
@@ -89,9 +90,21 @@ const CheckboxComponent: React.FC<CheckboxProps> = ({
|
|
|
89
90
|
style,
|
|
90
91
|
labelStyle,
|
|
91
92
|
accessibilityLabel,
|
|
93
|
+
nativeID,
|
|
92
94
|
testID,
|
|
93
95
|
}) => {
|
|
94
96
|
const theme = useTheme();
|
|
97
|
+
// The label is ADJACENT — the words beside the box ARE the control, so they
|
|
98
|
+
// name it and a `Field` around it only supplies what is missing. `disabled`
|
|
99
|
+
// is the other direction: the field's constrains, so it is OR-ed in.
|
|
100
|
+
const field = useFieldMembership({
|
|
101
|
+
accessibilityLabel,
|
|
102
|
+
label,
|
|
103
|
+
labelPlacement: 'adjacent',
|
|
104
|
+
disabled,
|
|
105
|
+
nativeID,
|
|
106
|
+
});
|
|
107
|
+
const isDisabled = field.disabled;
|
|
95
108
|
useInteractiveWebCss(STYLE_ID, BLOOM_CHECKBOX_CSS);
|
|
96
109
|
useInteractiveWebCss(CHECKBOX_GLYPH_STYLE_ID, CHECKBOX_GLYPH_CSS);
|
|
97
110
|
const { state: hovered, onIn: onHoverIn, onOut: onHoverOut } = useInteractionState();
|
|
@@ -100,13 +113,13 @@ const CheckboxComponent: React.FC<CheckboxProps> = ({
|
|
|
100
113
|
const { state: pressed, onIn: onPressIn, onOut: onPressOut } = useInteractionState();
|
|
101
114
|
const sizeConfig = CHECKBOX_SIZE_CONFIG[size];
|
|
102
115
|
const paint = useMemo(() => resolveCheckboxPaint(theme, color), [theme, color]);
|
|
103
|
-
const highlighted = !
|
|
116
|
+
const highlighted = !isDisabled && (hovered || pressed);
|
|
104
117
|
|
|
105
118
|
const handlePress = useCallback(() => {
|
|
106
|
-
if (!
|
|
119
|
+
if (!isDisabled) {
|
|
107
120
|
onCheckedChange(!checked);
|
|
108
121
|
}
|
|
109
|
-
}, [checked,
|
|
122
|
+
}, [checked, isDisabled, onCheckedChange]);
|
|
110
123
|
|
|
111
124
|
const hasText = Boolean(label || description);
|
|
112
125
|
|
|
@@ -135,11 +148,14 @@ const CheckboxComponent: React.FC<CheckboxProps> = ({
|
|
|
135
148
|
: {})}
|
|
136
149
|
style={[rowStyle, style]}
|
|
137
150
|
onPress={handlePress}
|
|
138
|
-
onPressIn={
|
|
139
|
-
onPressOut={
|
|
151
|
+
onPressIn={isDisabled ? undefined : onPressIn}
|
|
152
|
+
onPressOut={isDisabled ? undefined : onPressOut}
|
|
140
153
|
onHoverIn={onHoverIn}
|
|
141
154
|
onHoverOut={onHoverOut}
|
|
142
|
-
disabled={
|
|
155
|
+
disabled={isDisabled}
|
|
156
|
+
nativeID={field.nativeID}
|
|
157
|
+
aria-describedby={field.describedBy}
|
|
158
|
+
aria-invalid={field.invalid || undefined}
|
|
143
159
|
accessibilityRole="checkbox"
|
|
144
160
|
// `aria-checked`, not `accessibilityState`: react-native-web's
|
|
145
161
|
// `createDOMProps` never reads `accessibilityState`, so a checkbox that
|
|
@@ -149,7 +165,7 @@ const CheckboxComponent: React.FC<CheckboxProps> = ({
|
|
|
149
165
|
// this one prop is the spelling both platforms honour. `disabled`
|
|
150
166
|
// travels on the `disabled` prop above, which both platforms map.
|
|
151
167
|
aria-checked={indeterminate ? 'mixed' : checked}
|
|
152
|
-
accessibilityLabel={accessibilityLabel
|
|
168
|
+
accessibilityLabel={field.accessibilityLabel}
|
|
153
169
|
hitSlop={{ top: slop, bottom: slop, left: slop, right: slop }}
|
|
154
170
|
testID={testID}
|
|
155
171
|
>
|
|
@@ -157,7 +173,7 @@ const CheckboxComponent: React.FC<CheckboxProps> = ({
|
|
|
157
173
|
size={size}
|
|
158
174
|
checked={checked}
|
|
159
175
|
indeterminate={indeterminate}
|
|
160
|
-
disabled={
|
|
176
|
+
disabled={isDisabled}
|
|
161
177
|
highlighted={highlighted}
|
|
162
178
|
paint={paint}
|
|
163
179
|
// Centre the box on the label's first line box.
|
package/src/checkbox/types.ts
CHANGED
|
@@ -22,6 +22,11 @@ export interface CheckboxProps {
|
|
|
22
22
|
style?: StyleProp<ViewStyle>;
|
|
23
23
|
labelStyle?: StyleProp<TextStyle>;
|
|
24
24
|
accessibilityLabel?: string;
|
|
25
|
+
/**
|
|
26
|
+
* The control's id. Inside a `Field` the field supplies one, so the label's
|
|
27
|
+
* `htmlFor` points at this control on web.
|
|
28
|
+
*/
|
|
29
|
+
nativeID?: string;
|
|
25
30
|
testID?: string;
|
|
26
31
|
}
|
|
27
32
|
|
|
@@ -9,6 +9,8 @@ import { TYPE_SCALE } from '../typography';
|
|
|
9
9
|
import { resolveCalendarPalette } from './palette';
|
|
10
10
|
import { formatTime, parseTime, snapTime, stepTime } from './time';
|
|
11
11
|
import type { TimeFieldProps, TimeFieldSize } from './types';
|
|
12
|
+
import { useInheritedControl } from '../control-surface';
|
|
13
|
+
import { useFieldMembership } from '../field/membership';
|
|
12
14
|
|
|
13
15
|
/**
|
|
14
16
|
* A time of day, typed. `DatePicker` is the day; this is the hour and minute,
|
|
@@ -60,8 +62,8 @@ function TimeFieldComponent({
|
|
|
60
62
|
max,
|
|
61
63
|
step = 1,
|
|
62
64
|
hourFormat = '24h',
|
|
63
|
-
size
|
|
64
|
-
disabled = false,
|
|
65
|
+
size: sizeProp,
|
|
66
|
+
disabled: disabledProp = false,
|
|
65
67
|
accessibilityLabel,
|
|
66
68
|
placeholder,
|
|
67
69
|
width,
|
|
@@ -69,6 +71,12 @@ function TimeFieldComponent({
|
|
|
69
71
|
testID,
|
|
70
72
|
}: TimeFieldProps) {
|
|
71
73
|
const theme = useTheme();
|
|
74
|
+
// `"--:--"` names nothing, so the name is a prop — or, inside a `Field`, the
|
|
75
|
+
// field's label. The density contract supplies the size the same way: a
|
|
76
|
+
// container can ask for `small` once instead of on every control.
|
|
77
|
+
const size = useInheritedControl('density', sizeProp, 'medium');
|
|
78
|
+
const field = useFieldMembership({ accessibilityLabel, disabled: disabledProp });
|
|
79
|
+
const disabled = field.disabled;
|
|
72
80
|
const palette = useMemo(() => resolveCalendarPalette(theme), [theme]);
|
|
73
81
|
const config = SIZE_CONFIG[size];
|
|
74
82
|
|
|
@@ -150,7 +158,10 @@ function TimeFieldComponent({
|
|
|
150
158
|
placeholder={placeholder ?? (hourFormat === '12h' ? '--:-- --' : '--:--')}
|
|
151
159
|
placeholderTextColor={palette.secondaryText}
|
|
152
160
|
inputMode={hourFormat === '12h' ? 'text' : 'numeric'}
|
|
153
|
-
|
|
161
|
+
nativeID={field.nativeID}
|
|
162
|
+
accessibilityLabel={field.accessibilityLabel}
|
|
163
|
+
aria-describedby={field.describedBy}
|
|
164
|
+
aria-invalid={field.invalid || undefined}
|
|
154
165
|
aria-disabled={disabled || undefined}
|
|
155
166
|
style={[FIELD_TRANSITION, fieldStyle, style]}
|
|
156
167
|
testID={testID}
|
package/src/date-picker/types.ts
CHANGED
|
@@ -207,10 +207,13 @@ export interface TimeFieldProps {
|
|
|
207
207
|
size?: TimeFieldSize;
|
|
208
208
|
disabled?: boolean;
|
|
209
209
|
/**
|
|
210
|
-
* The NAME of the time ("Viewing time", "Check-in").
|
|
211
|
-
*
|
|
210
|
+
* The NAME of the time ("Viewing time", "Check-in").
|
|
211
|
+
*
|
|
212
|
+
* The field draws no label of its own and `"--:--"` names nothing, so this or
|
|
213
|
+
* an enclosing `Field`'s label is the only route to a name. With neither it
|
|
214
|
+
* warns once in development (`hooks/use-accessible-name-warning.ts`).
|
|
212
215
|
*/
|
|
213
|
-
accessibilityLabel
|
|
216
|
+
accessibilityLabel?: string;
|
|
214
217
|
/** The text while empty. Default `"--:--"`, or `"--:-- --"` in 12h. */
|
|
215
218
|
placeholder?: string;
|
|
216
219
|
/** Fixed width. Default: 104 at `medium`, 96 at `small`. */
|
|
@@ -4,6 +4,7 @@ import { View, type GestureResponderEvent } from 'react-native';
|
|
|
4
4
|
import { Text } from '../typography';
|
|
5
5
|
|
|
6
6
|
import { useTheme } from '../theme/use-theme';
|
|
7
|
+
import { ScreenScope } from '../layout';
|
|
7
8
|
import { Button } from '../button';
|
|
8
9
|
import type { ButtonVariant } from '../button/types';
|
|
9
10
|
import { useDialogContext } from './context';
|
|
@@ -40,7 +41,10 @@ export function DialogBody({
|
|
|
40
41
|
}) {
|
|
41
42
|
const theme = useTheme();
|
|
42
43
|
return (
|
|
43
|
-
|
|
44
|
+
// A dialog is its own screen: chrome inside it claims THIS surface's top
|
|
45
|
+
// edge and follows THIS surface's scroller, not the page's underneath
|
|
46
|
+
// (`layout/screen-scope.tsx`).
|
|
47
|
+
<ScreenScope>
|
|
44
48
|
{title ? (
|
|
45
49
|
// Bloom's `Text`, not react-native's: the raw one renders in whatever
|
|
46
50
|
// font the platform picks and takes no theme colour, so every dialog
|
|
@@ -74,7 +78,7 @@ export function DialogBody({
|
|
|
74
78
|
) : null}
|
|
75
79
|
{children}
|
|
76
80
|
{actions && actions.length > 0 ? <ActionRow actions={actions} /> : null}
|
|
77
|
-
|
|
81
|
+
</ScreenScope>
|
|
78
82
|
);
|
|
79
83
|
}
|
|
80
84
|
|
package/src/field/index.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export { Field } from './Field';
|
|
2
2
|
export { useFieldControl } from './context';
|
|
3
|
+
export { useFieldMembership } from './membership';
|
|
3
4
|
|
|
4
5
|
export type { FieldControlValue } from './context';
|
|
6
|
+
export type { FieldLabelPlacement, FieldMembership, FieldMembershipInput } from './membership';
|
|
5
7
|
export type { FieldProps } from './types';
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ONE resolution of field membership, for every control that opts in.
|
|
3
|
+
*
|
|
4
|
+
* `useFieldControl()` hands a control the field's ids and state; applying them
|
|
5
|
+
* is four separate decisions, and each has a direction that is silently wrong:
|
|
6
|
+
*
|
|
7
|
+
* the NAME defers to the field (`??`) — a control inside a labelled
|
|
8
|
+
* field must not announce nothing
|
|
9
|
+
* the CONSTRAINTS combine with the field (`||`) — `disabled` and `invalid`
|
|
10
|
+
* restrict, so a control cannot opt out
|
|
11
|
+
* the ID is the field's (`??` the caller's own, which wins)
|
|
12
|
+
* `describedBy` is a LIST, so the two are joined rather than one chosen
|
|
13
|
+
*
|
|
14
|
+
* Fifteen families resolving that by hand is fifteen chances to write `??` where
|
|
15
|
+
* `||` belongs — and the failure is silent in both directions: a control that
|
|
16
|
+
* re-enables itself inside a disabled field looks fine and a field-labelled
|
|
17
|
+
* control that announces nothing looks fine too. So it is a function, for the
|
|
18
|
+
* same reason `useInheritedControl` is one.
|
|
19
|
+
*
|
|
20
|
+
* `TextFieldInput` is the one family that resolves the same rule inline, because
|
|
21
|
+
* it also reconciles the text-field shell's own context and its standalone
|
|
22
|
+
* branch. It is not exempt from the RULE: `FieldMembership.test.tsx` asserts the
|
|
23
|
+
* observable properties for every family, so the two implementations cannot
|
|
24
|
+
* disagree about what a field means without going red.
|
|
25
|
+
*
|
|
26
|
+
* ## The name, and why placement decides it
|
|
27
|
+
*
|
|
28
|
+
* A control's own label is either STACKED above it (`TextField`, `Textarea`:
|
|
29
|
+
* the field's label and the control's label are two spellings of one thing, and
|
|
30
|
+
* a `Field` around it should win) or ADJACENT to it (a checkbox's caption, a
|
|
31
|
+
* radio's option text: the words the user reads AS the control, which the
|
|
32
|
+
* announced name must match). Getting that backwards makes a screen reader say
|
|
33
|
+
* one thing while the eye reads another, so it is a declared property of the
|
|
34
|
+
* family rather than a guess this hook could make.
|
|
35
|
+
*
|
|
36
|
+
* A control with neither — `Switch`, `Slider`, a progress bar — can be named by
|
|
37
|
+
* NOTHING but the field, which is the case this hook exists for.
|
|
38
|
+
*/
|
|
39
|
+
import { useMemo } from 'react';
|
|
40
|
+
|
|
41
|
+
import { useFieldControl } from './context';
|
|
42
|
+
|
|
43
|
+
/** Where the control renders its own label, which decides which name wins. */
|
|
44
|
+
export type FieldLabelPlacement = 'stacked' | 'adjacent';
|
|
45
|
+
|
|
46
|
+
export interface FieldMembershipInput {
|
|
47
|
+
/** The name the caller wrote on the control. Always wins. */
|
|
48
|
+
accessibilityLabel?: string;
|
|
49
|
+
/** The control's own label, when it has one. */
|
|
50
|
+
label?: string;
|
|
51
|
+
/**
|
|
52
|
+
* Whether that label sits above the control (`stacked`, the default — the
|
|
53
|
+
* field's label is the same thing said twice, so the field wins) or beside it
|
|
54
|
+
* as the control's visible text (`adjacent` — it wins, so the announced name
|
|
55
|
+
* and the words on screen cannot disagree).
|
|
56
|
+
*/
|
|
57
|
+
labelPlacement?: FieldLabelPlacement;
|
|
58
|
+
/** The control's own `disabled`. Combined with the field's, never replaced. */
|
|
59
|
+
disabled?: boolean;
|
|
60
|
+
/** The control's own invalid state. Combined with the field's. */
|
|
61
|
+
invalid?: boolean;
|
|
62
|
+
/** The control's own `required`. Combined with the field's. */
|
|
63
|
+
required?: boolean;
|
|
64
|
+
/** An id the caller wrote on the control. Outranks the field's. */
|
|
65
|
+
nativeID?: string;
|
|
66
|
+
/** An `aria-describedby` the caller wrote. JOINED with the field's, not replaced. */
|
|
67
|
+
describedBy?: string;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface FieldMembership {
|
|
71
|
+
/** Whether there is an enclosing `Field` at all. */
|
|
72
|
+
inField: boolean;
|
|
73
|
+
/** The id to render, or `undefined` when neither side has one. */
|
|
74
|
+
nativeID?: string;
|
|
75
|
+
/** The accessible name to render, or `undefined` when nothing names it. */
|
|
76
|
+
accessibilityLabel?: string;
|
|
77
|
+
/** The `aria-describedby` id list, or `undefined` when there is nothing to describe it. */
|
|
78
|
+
describedBy?: string;
|
|
79
|
+
/** The effective disabled state: the control's OR the field's. */
|
|
80
|
+
disabled: boolean;
|
|
81
|
+
/** The effective invalid state: the control's OR the field's. */
|
|
82
|
+
invalid: boolean;
|
|
83
|
+
/** The effective required state: the control's OR the field's. */
|
|
84
|
+
required: boolean;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Resolve what an enclosing `Field` contributes to one control.
|
|
89
|
+
*
|
|
90
|
+
* ```tsx
|
|
91
|
+
* const field = useFieldMembership({ accessibilityLabel, label, labelPlacement: 'adjacent', disabled });
|
|
92
|
+
* <Pressable
|
|
93
|
+
* nativeID={field.nativeID}
|
|
94
|
+
* accessibilityLabel={field.accessibilityLabel}
|
|
95
|
+
* aria-describedby={field.describedBy}
|
|
96
|
+
* aria-invalid={field.invalid || undefined}
|
|
97
|
+
* disabled={field.disabled}
|
|
98
|
+
* />
|
|
99
|
+
* ```
|
|
100
|
+
*
|
|
101
|
+
* Outside a `Field` it returns the control's own values unchanged, so a control
|
|
102
|
+
* can call it unconditionally.
|
|
103
|
+
*/
|
|
104
|
+
export function useFieldMembership(input: FieldMembershipInput = {}): FieldMembership {
|
|
105
|
+
const field = useFieldControl();
|
|
106
|
+
const {
|
|
107
|
+
accessibilityLabel,
|
|
108
|
+
label,
|
|
109
|
+
labelPlacement = 'stacked',
|
|
110
|
+
disabled = false,
|
|
111
|
+
invalid = false,
|
|
112
|
+
required = false,
|
|
113
|
+
nativeID,
|
|
114
|
+
describedBy,
|
|
115
|
+
} = input;
|
|
116
|
+
|
|
117
|
+
const fieldLabel = field?.labelText;
|
|
118
|
+
const fieldDescribedBy = field?.describedBy;
|
|
119
|
+
const fieldControlId = field?.controlId;
|
|
120
|
+
const fieldDisabled = field?.disabled === true;
|
|
121
|
+
const fieldInvalid = field?.invalid === true;
|
|
122
|
+
const fieldRequired = field?.required === true;
|
|
123
|
+
|
|
124
|
+
return useMemo<FieldMembership>(() => {
|
|
125
|
+
const own = label ?? undefined;
|
|
126
|
+
const name =
|
|
127
|
+
accessibilityLabel ??
|
|
128
|
+
(labelPlacement === 'adjacent' ? (own ?? fieldLabel) : (fieldLabel ?? own));
|
|
129
|
+
|
|
130
|
+
// An id LIST: a control that already points at its own hint keeps it, and
|
|
131
|
+
// the field's error is added rather than swapped in. Duplicates are
|
|
132
|
+
// dropped — an id named twice is read twice by some screen readers.
|
|
133
|
+
const described = [describedBy, fieldDescribedBy].filter(
|
|
134
|
+
(id): id is string => typeof id === 'string' && id.length > 0,
|
|
135
|
+
);
|
|
136
|
+
const uniqueDescribed = Array.from(new Set(described.flatMap((ids) => ids.split(/\s+/))));
|
|
137
|
+
|
|
138
|
+
return {
|
|
139
|
+
inField: field != null,
|
|
140
|
+
nativeID: nativeID ?? fieldControlId,
|
|
141
|
+
accessibilityLabel: name,
|
|
142
|
+
describedBy: uniqueDescribed.length > 0 ? uniqueDescribed.join(' ') : undefined,
|
|
143
|
+
disabled: disabled || fieldDisabled,
|
|
144
|
+
invalid: invalid || fieldInvalid,
|
|
145
|
+
required: required || fieldRequired,
|
|
146
|
+
};
|
|
147
|
+
}, [
|
|
148
|
+
field,
|
|
149
|
+
accessibilityLabel,
|
|
150
|
+
label,
|
|
151
|
+
labelPlacement,
|
|
152
|
+
disabled,
|
|
153
|
+
invalid,
|
|
154
|
+
required,
|
|
155
|
+
nativeID,
|
|
156
|
+
describedBy,
|
|
157
|
+
fieldLabel,
|
|
158
|
+
fieldDescribedBy,
|
|
159
|
+
fieldControlId,
|
|
160
|
+
fieldDisabled,
|
|
161
|
+
fieldInvalid,
|
|
162
|
+
fieldRequired,
|
|
163
|
+
]);
|
|
164
|
+
}
|
|
@@ -92,6 +92,7 @@ type StaggerState = 'shown' | 'hiding' | 'hidden';
|
|
|
92
92
|
|
|
93
93
|
import { formatFileSize } from './shared';
|
|
94
94
|
import { DISABLED_OPACITY } from '../styles/tokens';
|
|
95
|
+
import { useFieldMembership } from '../field/membership';
|
|
95
96
|
|
|
96
97
|
export { formatFileSize };
|
|
97
98
|
|
|
@@ -445,12 +446,19 @@ const FileUploadComponent = function FileUpload({
|
|
|
445
446
|
maxBytes = DEFAULT_MAX_BYTES,
|
|
446
447
|
renderFileIcon,
|
|
447
448
|
labels: labelOverrides,
|
|
448
|
-
disabled = false,
|
|
449
|
-
accessibilityLabel
|
|
449
|
+
disabled: disabledProp = false,
|
|
450
|
+
accessibilityLabel,
|
|
450
451
|
style,
|
|
451
452
|
testID,
|
|
452
453
|
}: FileUploadProps) {
|
|
453
454
|
const theme = useTheme();
|
|
455
|
+
// The drop zone draws a prompt but names itself with a prop, so a `Field`
|
|
456
|
+
// around it is the other place the name can come from — and the field's
|
|
457
|
+
// `disabled` blocks the picker whatever the caller passed. `'Upload a file'`
|
|
458
|
+
// stays the last resort rather than a default that would outrank a label the
|
|
459
|
+
// user can read.
|
|
460
|
+
const field = useFieldMembership({ accessibilityLabel, disabled: disabledProp });
|
|
461
|
+
const disabled = field.disabled;
|
|
454
462
|
useInteractiveWebCss(STYLE_ID, BLOOM_FILE_UPLOAD_CSS);
|
|
455
463
|
const reducedMotion = useReducedMotion();
|
|
456
464
|
const paint = useMemo(() => resolveFileUploadPaint(theme), [theme]);
|
|
@@ -644,7 +652,10 @@ const FileUploadComponent = function FileUpload({
|
|
|
644
652
|
ref={rootRef}
|
|
645
653
|
{...webDataSet({ bloomFileUpload: busy ? 'busy' : 'idle' })}
|
|
646
654
|
accessibilityRole="button"
|
|
647
|
-
|
|
655
|
+
nativeID={field.nativeID}
|
|
656
|
+
accessibilityLabel={field.accessibilityLabel ?? 'Upload a file'}
|
|
657
|
+
aria-describedby={field.describedBy}
|
|
658
|
+
aria-invalid={field.invalid || undefined}
|
|
648
659
|
aria-busy={busy}
|
|
649
660
|
disabled={disabled}
|
|
650
661
|
focusable={!blocked}
|
package/src/floating/types.ts
CHANGED
|
@@ -162,6 +162,19 @@ export interface TriggerHandleProps {
|
|
|
162
162
|
accessibilityLabel?: string;
|
|
163
163
|
accessibilityRole?: 'button';
|
|
164
164
|
'aria-expanded'?: boolean;
|
|
165
|
+
/**
|
|
166
|
+
* The trigger's id, so an enclosing `Field`'s label can point at it.
|
|
167
|
+
*
|
|
168
|
+
* Only a trigger that IS a form control sets it — `Select` does; a `Popover`
|
|
169
|
+
* or a `DropdownMenu` inside a field is not the field's control and must not
|
|
170
|
+
* take its id. `TriggerSlot` therefore does not read the field context
|
|
171
|
+
* itself; each family decides whether it is the control.
|
|
172
|
+
*/
|
|
173
|
+
nativeID?: string;
|
|
174
|
+
/** The field's hint or error, for a trigger that is a form control. */
|
|
175
|
+
'aria-describedby'?: string;
|
|
176
|
+
/** The field's invalid state, for a trigger that is a form control. */
|
|
177
|
+
'aria-invalid'?: boolean;
|
|
165
178
|
/**
|
|
166
179
|
* What this trigger OPENS. Set by each family from its own constant in
|
|
167
180
|
* `floating/constants.ts`, never defaulted here — the five families open three
|
package/src/index.ts
CHANGED
|
@@ -167,6 +167,7 @@ export {
|
|
|
167
167
|
useTopEdgeInset,
|
|
168
168
|
ScrollOffsetProvider,
|
|
169
169
|
useScrollOffset,
|
|
170
|
+
ScreenScope,
|
|
170
171
|
} from './layout';
|
|
171
172
|
export { Fab } from './fab';
|
|
172
173
|
export type { FabProps, FabVariant, FabSize, FabPlacement } from './fab';
|
|
@@ -282,8 +283,14 @@ export * from './date-picker';
|
|
|
282
283
|
export { Search } from './search';
|
|
283
284
|
export { Label } from './label';
|
|
284
285
|
export type { LabelProps } from './label';
|
|
285
|
-
export { Field } from './field';
|
|
286
|
-
export type {
|
|
286
|
+
export { Field, useFieldControl, useFieldMembership } from './field';
|
|
287
|
+
export type {
|
|
288
|
+
FieldControlValue,
|
|
289
|
+
FieldLabelPlacement,
|
|
290
|
+
FieldMembership,
|
|
291
|
+
FieldMembershipInput,
|
|
292
|
+
FieldProps,
|
|
293
|
+
} from './field';
|
|
287
294
|
export { InputGroup, InputGroupAddon } from './input-group';
|
|
288
295
|
export type { InputGroupProps, InputGroupAddonProps } from './input-group';
|
|
289
296
|
export { RangeSlider, Slider } from './slider';
|
package/src/index.web.ts
CHANGED
|
@@ -172,6 +172,7 @@ export {
|
|
|
172
172
|
useTopEdgeInset,
|
|
173
173
|
ScrollOffsetProvider,
|
|
174
174
|
useScrollOffset,
|
|
175
|
+
ScreenScope,
|
|
175
176
|
} from './layout';
|
|
176
177
|
export { Fab } from './fab/index.web';
|
|
177
178
|
export type { FabProps, FabVariant, FabSize, FabPlacement } from './fab/index.web';
|
|
@@ -287,8 +288,14 @@ export * from './date-picker';
|
|
|
287
288
|
export { Search } from './search';
|
|
288
289
|
export { Label } from './label';
|
|
289
290
|
export type { LabelProps } from './label';
|
|
290
|
-
export { Field } from './field';
|
|
291
|
-
export type {
|
|
291
|
+
export { Field, useFieldControl, useFieldMembership } from './field';
|
|
292
|
+
export type {
|
|
293
|
+
FieldControlValue,
|
|
294
|
+
FieldLabelPlacement,
|
|
295
|
+
FieldMembership,
|
|
296
|
+
FieldMembershipInput,
|
|
297
|
+
FieldProps,
|
|
298
|
+
} from './field';
|
|
292
299
|
export { InputGroup, InputGroupAddon } from './input-group';
|
|
293
300
|
export type { InputGroupProps, InputGroupAddonProps } from './input-group';
|
|
294
301
|
export { RangeSlider, Slider } from './slider';
|
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
} from '../text-field/shared';
|
|
25
25
|
import { TextFieldGroupContext } from '../text-field/TextField';
|
|
26
26
|
import type { InputGroupAddonProps, InputGroupProps } from './types';
|
|
27
|
+
import { useFieldMembership } from '../field/membership';
|
|
27
28
|
|
|
28
29
|
/**
|
|
29
30
|
* The group is the input shell (`base/input/input.tsx`) with its adornment
|
|
@@ -149,12 +150,21 @@ InputGroupAddon.displayName = 'InputGroupAddon';
|
|
|
149
150
|
*/
|
|
150
151
|
const InputGroupComponent = function InputGroup({
|
|
151
152
|
children,
|
|
152
|
-
isInvalid = false,
|
|
153
|
-
disabled = false,
|
|
153
|
+
isInvalid: isInvalidProp = false,
|
|
154
|
+
disabled: disabledProp = false,
|
|
154
155
|
size = 'md',
|
|
155
156
|
style,
|
|
156
157
|
testID,
|
|
157
158
|
}: InputGroupProps) {
|
|
159
|
+
// A group holding a control and its addons is ONE control as far as a `Field`
|
|
160
|
+
// is concerned: the field's `disabled` and invalid state reach every member
|
|
161
|
+
// through the group's own contexts, including an addon's `Button`, which reads
|
|
162
|
+
// neither the field nor the text-field group. The group publishes no name — it
|
|
163
|
+
// does not know which of its children is the control the label points at, so
|
|
164
|
+
// that stays the field's `nativeID` and the input's to apply.
|
|
165
|
+
const member = useFieldMembership({ disabled: disabledProp, invalid: isInvalidProp });
|
|
166
|
+
const disabled = member.disabled;
|
|
167
|
+
const isInvalid = member.invalid;
|
|
158
168
|
const cfg = SIZE_CONFIG[size];
|
|
159
169
|
const { state: focused, onIn: onFocus, onOut: onBlur } = useInteractionState();
|
|
160
170
|
const { state: hovered, onIn: onHoverIn, onOut: onHoverOut } =
|
|
@@ -211,6 +221,7 @@ const InputGroupComponent = function InputGroup({
|
|
|
211
221
|
// react-native-web has no `disabled` prop to derive the attribute from
|
|
212
222
|
// and never reads `accessibilityState`. React Native folds it back.
|
|
213
223
|
aria-disabled={disabled || undefined}
|
|
224
|
+
aria-invalid={isInvalid || undefined}
|
|
214
225
|
// Capture focus bubbling from a nested input so the whole chrome
|
|
215
226
|
// reflects focus — RN-web bubbles focus/blur, native does not but a
|
|
216
227
|
// nested TextInput's own focus ring is sufficient there.
|