@oxyhq/bloom 0.87.1 → 0.89.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/lib/commonjs/accordion/Accordion.js +7 -5
- package/lib/commonjs/accordion/Accordion.js.map +1 -1
- package/lib/commonjs/bottom-sheet/BottomSheetBase.js +7 -12
- package/lib/commonjs/bottom-sheet/BottomSheetBase.js.map +1 -1
- package/lib/commonjs/button/Button.js +8 -7
- package/lib/commonjs/button/Button.js.map +1 -1
- package/lib/commonjs/button/Button.web.js +2 -6
- package/lib/commonjs/button/Button.web.js.map +1 -1
- package/lib/commonjs/checkbox/Checkbox.js +10 -5
- package/lib/commonjs/checkbox/Checkbox.js.map +1 -1
- package/lib/commonjs/chip/Chip.js +10 -1
- package/lib/commonjs/chip/Chip.js.map +1 -1
- package/lib/commonjs/collapsible/Collapsible.js +5 -4
- package/lib/commonjs/collapsible/Collapsible.js.map +1 -1
- package/lib/commonjs/composition-bar/CompositionBar.js +6 -1
- package/lib/commonjs/composition-bar/CompositionBar.js.map +1 -1
- package/lib/commonjs/connection-dots/ConnectionDots.web.js +2 -6
- package/lib/commonjs/connection-dots/ConnectionDots.web.js.map +1 -1
- package/lib/commonjs/design-tokens/index.js +19 -0
- package/lib/commonjs/design-tokens/index.js.map +1 -1
- package/lib/commonjs/design-tokens/tokens-json.js +149 -0
- package/lib/commonjs/design-tokens/tokens-json.js.map +1 -0
- package/lib/commonjs/design-tokens/tokens.json +6681 -0
- package/lib/commonjs/dialog/Dialog.web.js +2 -6
- package/lib/commonjs/dialog/Dialog.web.js.map +1 -1
- package/lib/commonjs/fab/Fab.web.js +2 -6
- package/lib/commonjs/fab/Fab.web.js.map +1 -1
- package/lib/commonjs/fonts/apply-font-faces.web.js +8 -10
- package/lib/commonjs/fonts/apply-font-faces.web.js.map +1 -1
- package/lib/commonjs/frosted-icon-button/FrostedIconButton.js +7 -1
- package/lib/commonjs/frosted-icon-button/FrostedIconButton.js.map +1 -1
- package/lib/commonjs/frosted-icon-button/FrostedIconButton.web.js +2 -6
- package/lib/commonjs/frosted-icon-button/FrostedIconButton.web.js.map +1 -1
- package/lib/commonjs/input-group/InputGroup.js +6 -4
- package/lib/commonjs/input-group/InputGroup.js.map +1 -1
- package/lib/commonjs/item/Item.js +23 -5
- package/lib/commonjs/item/Item.js.map +1 -1
- package/lib/commonjs/loading/SpinnerIcon.web.js +2 -6
- package/lib/commonjs/loading/SpinnerIcon.web.js.map +1 -1
- package/lib/commonjs/segmented-control/index.js +12 -5
- package/lib/commonjs/segmented-control/index.js.map +1 -1
- package/lib/commonjs/select/index.js +6 -4
- package/lib/commonjs/select/index.js.map +1 -1
- package/lib/commonjs/select/index.web.js +6 -4
- package/lib/commonjs/select/index.web.js.map +1 -1
- package/lib/commonjs/settings-list/SettingsList.js +6 -4
- package/lib/commonjs/settings-list/SettingsList.js.map +1 -1
- package/lib/commonjs/slider/Slider.js +12 -9
- package/lib/commonjs/slider/Slider.js.map +1 -1
- package/lib/commonjs/styles/adopt-style-sheet.js +126 -0
- package/lib/commonjs/styles/adopt-style-sheet.js.map +1 -0
- package/lib/commonjs/switch/Switch.js +9 -5
- package/lib/commonjs/switch/Switch.js.map +1 -1
- package/lib/commonjs/tab-bar/TabBarBase.js +6 -4
- package/lib/commonjs/tab-bar/TabBarBase.js.map +1 -1
- package/lib/module/accordion/Accordion.js +7 -5
- package/lib/module/accordion/Accordion.js.map +1 -1
- package/lib/module/bottom-sheet/BottomSheetBase.js +7 -12
- package/lib/module/bottom-sheet/BottomSheetBase.js.map +1 -1
- package/lib/module/button/Button.js +8 -7
- package/lib/module/button/Button.js.map +1 -1
- package/lib/module/button/Button.web.js +2 -6
- package/lib/module/button/Button.web.js.map +1 -1
- package/lib/module/checkbox/Checkbox.js +10 -5
- package/lib/module/checkbox/Checkbox.js.map +1 -1
- package/lib/module/chip/Chip.js +10 -1
- package/lib/module/chip/Chip.js.map +1 -1
- package/lib/module/collapsible/Collapsible.js +5 -4
- package/lib/module/collapsible/Collapsible.js.map +1 -1
- package/lib/module/composition-bar/CompositionBar.js +6 -1
- package/lib/module/composition-bar/CompositionBar.js.map +1 -1
- package/lib/module/connection-dots/ConnectionDots.web.js +2 -6
- package/lib/module/connection-dots/ConnectionDots.web.js.map +1 -1
- package/lib/module/design-tokens/index.js +5 -0
- package/lib/module/design-tokens/index.js.map +1 -1
- package/lib/module/design-tokens/tokens-json.js +144 -0
- package/lib/module/design-tokens/tokens-json.js.map +1 -0
- package/lib/module/design-tokens/tokens.json +6681 -0
- package/lib/module/dialog/Dialog.web.js +2 -6
- package/lib/module/dialog/Dialog.web.js.map +1 -1
- package/lib/module/fab/Fab.web.js +2 -6
- package/lib/module/fab/Fab.web.js.map +1 -1
- package/lib/module/fonts/apply-font-faces.web.js +8 -10
- package/lib/module/fonts/apply-font-faces.web.js.map +1 -1
- package/lib/module/frosted-icon-button/FrostedIconButton.js +7 -1
- package/lib/module/frosted-icon-button/FrostedIconButton.js.map +1 -1
- package/lib/module/frosted-icon-button/FrostedIconButton.web.js +2 -6
- package/lib/module/frosted-icon-button/FrostedIconButton.web.js.map +1 -1
- package/lib/module/input-group/InputGroup.js +6 -4
- package/lib/module/input-group/InputGroup.js.map +1 -1
- package/lib/module/item/Item.js +23 -5
- package/lib/module/item/Item.js.map +1 -1
- package/lib/module/loading/SpinnerIcon.web.js +2 -6
- package/lib/module/loading/SpinnerIcon.web.js.map +1 -1
- package/lib/module/segmented-control/index.js +12 -5
- package/lib/module/segmented-control/index.js.map +1 -1
- package/lib/module/select/index.js +6 -4
- package/lib/module/select/index.js.map +1 -1
- package/lib/module/select/index.web.js +6 -4
- package/lib/module/select/index.web.js.map +1 -1
- package/lib/module/settings-list/SettingsList.js +6 -4
- package/lib/module/settings-list/SettingsList.js.map +1 -1
- package/lib/module/slider/Slider.js +12 -9
- package/lib/module/slider/Slider.js.map +1 -1
- package/lib/module/styles/adopt-style-sheet.js +121 -0
- package/lib/module/styles/adopt-style-sheet.js.map +1 -0
- package/lib/module/switch/Switch.js +9 -5
- package/lib/module/switch/Switch.js.map +1 -1
- package/lib/module/tab-bar/TabBarBase.js +6 -4
- package/lib/module/tab-bar/TabBarBase.js.map +1 -1
- package/lib/typescript/commonjs/accordion/Accordion.d.ts.map +1 -1
- package/lib/typescript/commonjs/bottom-sheet/BottomSheetBase.d.ts.map +1 -1
- package/lib/typescript/commonjs/button/Button.d.ts.map +1 -1
- package/lib/typescript/commonjs/button/Button.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/checkbox/Checkbox.d.ts.map +1 -1
- package/lib/typescript/commonjs/chip/Chip.d.ts.map +1 -1
- package/lib/typescript/commonjs/collapsible/Collapsible.d.ts.map +1 -1
- package/lib/typescript/commonjs/composition-bar/CompositionBar.d.ts.map +1 -1
- package/lib/typescript/commonjs/connection-dots/ConnectionDots.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/design-tokens/index.d.ts +2 -0
- package/lib/typescript/commonjs/design-tokens/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/design-tokens/tokens-json.d.ts +85 -0
- package/lib/typescript/commonjs/design-tokens/tokens-json.d.ts.map +1 -0
- package/lib/typescript/commonjs/dialog/Dialog.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/fab/Fab.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/fonts/apply-font-faces.web.d.ts +5 -3
- package/lib/typescript/commonjs/fonts/apply-font-faces.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/frosted-icon-button/FrostedIconButton.d.ts.map +1 -1
- package/lib/typescript/commonjs/frosted-icon-button/FrostedIconButton.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/input-group/InputGroup.d.ts.map +1 -1
- package/lib/typescript/commonjs/item/Item.d.ts.map +1 -1
- package/lib/typescript/commonjs/loading/SpinnerIcon.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/segmented-control/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/select/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/select/index.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/settings-list/SettingsList.d.ts.map +1 -1
- package/lib/typescript/commonjs/slider/Slider.d.ts.map +1 -1
- package/lib/typescript/commonjs/styles/adopt-style-sheet.d.ts +22 -0
- package/lib/typescript/commonjs/styles/adopt-style-sheet.d.ts.map +1 -0
- package/lib/typescript/commonjs/switch/Switch.d.ts.map +1 -1
- package/lib/typescript/commonjs/tab-bar/TabBarBase.d.ts.map +1 -1
- package/lib/typescript/module/accordion/Accordion.d.ts.map +1 -1
- package/lib/typescript/module/bottom-sheet/BottomSheetBase.d.ts.map +1 -1
- package/lib/typescript/module/button/Button.d.ts.map +1 -1
- package/lib/typescript/module/button/Button.web.d.ts.map +1 -1
- package/lib/typescript/module/checkbox/Checkbox.d.ts.map +1 -1
- package/lib/typescript/module/chip/Chip.d.ts.map +1 -1
- package/lib/typescript/module/collapsible/Collapsible.d.ts.map +1 -1
- package/lib/typescript/module/composition-bar/CompositionBar.d.ts.map +1 -1
- package/lib/typescript/module/connection-dots/ConnectionDots.web.d.ts.map +1 -1
- package/lib/typescript/module/design-tokens/index.d.ts +2 -0
- package/lib/typescript/module/design-tokens/index.d.ts.map +1 -1
- package/lib/typescript/module/design-tokens/tokens-json.d.ts +85 -0
- package/lib/typescript/module/design-tokens/tokens-json.d.ts.map +1 -0
- package/lib/typescript/module/dialog/Dialog.web.d.ts.map +1 -1
- package/lib/typescript/module/fab/Fab.web.d.ts.map +1 -1
- package/lib/typescript/module/fonts/apply-font-faces.web.d.ts +5 -3
- package/lib/typescript/module/fonts/apply-font-faces.web.d.ts.map +1 -1
- package/lib/typescript/module/frosted-icon-button/FrostedIconButton.d.ts.map +1 -1
- package/lib/typescript/module/frosted-icon-button/FrostedIconButton.web.d.ts.map +1 -1
- package/lib/typescript/module/input-group/InputGroup.d.ts.map +1 -1
- package/lib/typescript/module/item/Item.d.ts.map +1 -1
- package/lib/typescript/module/loading/SpinnerIcon.web.d.ts.map +1 -1
- package/lib/typescript/module/segmented-control/index.d.ts.map +1 -1
- package/lib/typescript/module/select/index.d.ts.map +1 -1
- package/lib/typescript/module/select/index.web.d.ts.map +1 -1
- package/lib/typescript/module/settings-list/SettingsList.d.ts.map +1 -1
- package/lib/typescript/module/slider/Slider.d.ts.map +1 -1
- package/lib/typescript/module/styles/adopt-style-sheet.d.ts +22 -0
- package/lib/typescript/module/styles/adopt-style-sheet.d.ts.map +1 -0
- package/lib/typescript/module/switch/Switch.d.ts.map +1 -1
- package/lib/typescript/module/tab-bar/TabBarBase.d.ts.map +1 -1
- package/package.json +4 -2
- package/src/__tests__/Button.test.tsx +11 -3
- package/src/__tests__/InputGroup.test.tsx +4 -1
- package/src/__tests__/Item.test.tsx +5 -1
- package/src/__tests__/Slider.test.tsx +11 -2
- package/src/__tests__/TabBar.test.tsx +9 -1
- package/src/__tests__/TabBarNoSelection.test.tsx +3 -1
- package/src/__tests__/adopt-style-sheet.node.test.ts +21 -0
- package/src/__tests__/adopt-style-sheet.test.ts +165 -0
- package/src/__tests__/apply-font-faces.test.ts +32 -0
- package/src/__tests__/aria-state-web.test.tsx +466 -0
- package/src/__tests__/design-tokens-json.test.ts +112 -0
- package/src/__tests__/exports-map-contract.test.ts +5 -1
- package/src/__tests__/support/constructed-style-sheets.ts +68 -0
- package/src/accordion/Accordion.tsx +5 -1
- package/src/bottom-sheet/BottomSheetBase.tsx +10 -14
- package/src/button/Button.tsx +6 -1
- package/src/button/Button.web.tsx +2 -6
- package/src/checkbox/Checkbox.tsx +8 -1
- package/src/chip/Chip.tsx +8 -0
- package/src/collapsible/Collapsible.tsx +3 -1
- package/src/composition-bar/CompositionBar.tsx +4 -0
- package/src/connection-dots/ConnectionDots.web.tsx +2 -6
- package/src/design-tokens/index.ts +16 -0
- package/src/design-tokens/tokens-json.ts +214 -0
- package/src/design-tokens/tokens.json +6681 -0
- package/src/dialog/Dialog.web.tsx +2 -6
- package/src/fab/Fab.web.tsx +2 -6
- package/src/fonts/apply-font-faces.web.ts +11 -11
- package/src/frosted-icon-button/FrostedIconButton.tsx +5 -0
- package/src/frosted-icon-button/FrostedIconButton.web.tsx +2 -6
- package/src/input-group/InputGroup.tsx +4 -1
- package/src/item/Item.tsx +24 -2
- package/src/loading/SpinnerIcon.web.tsx +2 -6
- package/src/segmented-control/index.tsx +8 -1
- package/src/select/index.tsx +4 -1
- package/src/select/index.web.tsx +4 -1
- package/src/settings-list/SettingsList.tsx +4 -1
- package/src/slider/Slider.tsx +10 -2
- package/src/styles/adopt-style-sheet.ts +131 -0
- package/src/switch/Switch.tsx +7 -1
- package/src/tab-bar/TabBarBase.tsx +4 -1
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
type StyleProp,
|
|
12
12
|
} from 'react-native';
|
|
13
13
|
import { Gesture, GestureDetector, type GestureType } from 'react-native-gesture-handler';
|
|
14
|
+
import { adoptStyleSheet, dropStyleSheet } from '../styles/adopt-style-sheet';
|
|
14
15
|
import type { WebCssStyle } from '../styles/web-view-style';
|
|
15
16
|
import { Z_INDEX } from '../styles/z-index';
|
|
16
17
|
import Animated, {
|
|
@@ -921,15 +922,7 @@ const styles = StyleSheet.create({
|
|
|
921
922
|
|
|
922
923
|
// Create web scrollbar styles dynamically based on theme
|
|
923
924
|
const createWebScrollbarStyle = (borderColor: string) => {
|
|
924
|
-
if (Platform.OS !== 'web'
|
|
925
|
-
|
|
926
|
-
let styleElement = document.getElementById(SCROLLBAR_STYLE_ID) as HTMLStyleElement | null;
|
|
927
|
-
|
|
928
|
-
if (!styleElement) {
|
|
929
|
-
styleElement = document.createElement('style');
|
|
930
|
-
styleElement.id = SCROLLBAR_STYLE_ID;
|
|
931
|
-
document.head.appendChild(styleElement);
|
|
932
|
-
}
|
|
925
|
+
if (Platform.OS !== 'web') return;
|
|
933
926
|
|
|
934
927
|
// Derive a slightly darker scrollbar hover color from the border color
|
|
935
928
|
const scrollbarColor = borderColor;
|
|
@@ -937,7 +930,9 @@ const createWebScrollbarStyle = (borderColor: string) => {
|
|
|
937
930
|
? borderColor.replace(/\)$/, ' / 0.7)') // add alpha for hover
|
|
938
931
|
: '#888';
|
|
939
932
|
|
|
940
|
-
|
|
933
|
+
adoptStyleSheet(
|
|
934
|
+
SCROLLBAR_STYLE_ID,
|
|
935
|
+
`
|
|
941
936
|
.bottom-sheet-scrollview::-webkit-scrollbar {
|
|
942
937
|
width: 6px;
|
|
943
938
|
}
|
|
@@ -952,13 +947,14 @@ const createWebScrollbarStyle = (borderColor: string) => {
|
|
|
952
947
|
.bottom-sheet-scrollview::-webkit-scrollbar-thumb:hover {
|
|
953
948
|
background: ${scrollbarHoverColor};
|
|
954
949
|
}
|
|
955
|
-
|
|
950
|
+
`,
|
|
951
|
+
);
|
|
956
952
|
};
|
|
957
953
|
|
|
958
954
|
const SCROLLBAR_STYLE_ID = 'bottom-sheet-scrollbar-style';
|
|
959
955
|
|
|
960
|
-
/**
|
|
956
|
+
/** Detach the scrollbar rules on unmount. */
|
|
961
957
|
const removeWebScrollbarStyle = () => {
|
|
962
|
-
if (Platform.OS !== 'web'
|
|
963
|
-
|
|
958
|
+
if (Platform.OS !== 'web') return;
|
|
959
|
+
dropStyleSheet(SCROLLBAR_STYLE_ID);
|
|
964
960
|
};
|
package/src/button/Button.tsx
CHANGED
|
@@ -334,7 +334,12 @@ const ButtonComponent: React.FC<ButtonProps> = ({
|
|
|
334
334
|
accessibilityLabel={accessibilityLabel}
|
|
335
335
|
accessibilityHint={accessibilityHint}
|
|
336
336
|
accessibilityRole="button"
|
|
337
|
-
|
|
337
|
+
// `aria-busy` matches what `Button.web.tsx` emits, so a loading button
|
|
338
|
+
// announces the same thing whichever fork a bundler resolves.
|
|
339
|
+
// react-native-web never reads `accessibilityState`; React Native folds
|
|
340
|
+
// `aria-busy` back into it. The disabled half travels on the `disabled`
|
|
341
|
+
// prop above, which both platforms map.
|
|
342
|
+
aria-busy={loading || undefined}
|
|
338
343
|
testID={testID}
|
|
339
344
|
>
|
|
340
345
|
{loading ? (
|
|
@@ -12,6 +12,7 @@ import React, {
|
|
|
12
12
|
import { useTheme } from '../theme/use-theme';
|
|
13
13
|
import type { Theme } from '../theme/types';
|
|
14
14
|
import { SpinnerIcon } from '../loading/SpinnerIcon.web';
|
|
15
|
+
import { adoptStyleSheet } from '../styles/adopt-style-sheet';
|
|
15
16
|
import { flattenWebStyle } from '../styles/flatten-web-style';
|
|
16
17
|
import type { ButtonProps, ButtonSize, ButtonVariant } from './types';
|
|
17
18
|
|
|
@@ -128,12 +129,7 @@ const BLOOM_BUTTON_CSS = `
|
|
|
128
129
|
|
|
129
130
|
function useButtonCss(): void {
|
|
130
131
|
useEffect(() => {
|
|
131
|
-
|
|
132
|
-
if (document.getElementById(STYLE_ID)) return;
|
|
133
|
-
const style = document.createElement('style');
|
|
134
|
-
style.id = STYLE_ID;
|
|
135
|
-
style.textContent = BLOOM_BUTTON_CSS;
|
|
136
|
-
document.head.appendChild(style);
|
|
132
|
+
adoptStyleSheet(STYLE_ID, BLOOM_BUTTON_CSS);
|
|
137
133
|
}, []);
|
|
138
134
|
}
|
|
139
135
|
|
|
@@ -106,7 +106,14 @@ const CheckboxComponent: React.FC<CheckboxProps> = ({
|
|
|
106
106
|
onPressOut={onPressOut}
|
|
107
107
|
disabled={disabled}
|
|
108
108
|
accessibilityRole="checkbox"
|
|
109
|
-
|
|
109
|
+
// `aria-checked`, not `accessibilityState`: react-native-web's
|
|
110
|
+
// `createDOMProps` never reads `accessibilityState`, so a checkbox that
|
|
111
|
+
// set only that announced no state at all on web — the box was drawn
|
|
112
|
+
// checked while assistive tech saw an unchecked control. React Native's
|
|
113
|
+
// `Pressable` folds `aria-checked` back into `accessibilityState`, so
|
|
114
|
+
// this one prop is the spelling both platforms honour. `disabled`
|
|
115
|
+
// travels on the `disabled` prop above, which both platforms map.
|
|
116
|
+
aria-checked={indeterminate ? 'mixed' : checked}
|
|
110
117
|
accessibilityLabel={accessibilityLabel ?? label}
|
|
111
118
|
hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}
|
|
112
119
|
testID={testID}
|
package/src/chip/Chip.tsx
CHANGED
|
@@ -121,7 +121,15 @@ const ChipComponent: React.FC<ChipProps> = ({
|
|
|
121
121
|
disabled={disabled}
|
|
122
122
|
accessibilityLabel={accessibilityLabel}
|
|
123
123
|
accessibilityRole="button"
|
|
124
|
+
// Both spellings, because neither platform reads the other's and
|
|
125
|
+
// there is no single prop that serves both here: react-native-web
|
|
126
|
+
// ignores `accessibilityState`, while React Native has no
|
|
127
|
+
// `aria-pressed` at all (its `AccessibilityState` is
|
|
128
|
+
// disabled/selected/checked/busy/expanded). `aria-pressed` is also
|
|
129
|
+
// the state ARIA defines for a toggle with `role="button"` —
|
|
130
|
+
// `aria-selected` would be invalid on that role.
|
|
124
131
|
accessibilityState={{ disabled, selected }}
|
|
132
|
+
aria-pressed={selected}
|
|
125
133
|
testID={testID}
|
|
126
134
|
>
|
|
127
135
|
{content}
|
|
@@ -58,7 +58,9 @@ const CollapsibleComponent: React.FC<CollapsibleProps> = ({
|
|
|
58
58
|
onPress={handleToggle}
|
|
59
59
|
activeOpacity={0.7}
|
|
60
60
|
accessibilityRole="button"
|
|
61
|
-
|
|
61
|
+
// See `Accordion`: react-native-web reads `aria-expanded`, never
|
|
62
|
+
// `accessibilityState`; React Native folds it back into that state.
|
|
63
|
+
aria-expanded={isOpen}
|
|
62
64
|
style={styles.trigger}
|
|
63
65
|
>
|
|
64
66
|
{chevronIcon ?? (
|
|
@@ -75,7 +75,11 @@ const CompositionBarComponent: React.FC<CompositionBarProps> = ({
|
|
|
75
75
|
onPress={() => onSelect(category.key)}
|
|
76
76
|
accessibilityRole="button"
|
|
77
77
|
accessibilityLabel={category.name}
|
|
78
|
+
// See `Chip`: `accessibilityState` carries this on native (which
|
|
79
|
+
// has no `aria-pressed`), `aria-pressed` on web (where
|
|
80
|
+
// react-native-web never reads `accessibilityState`).
|
|
78
81
|
accessibilityState={{ selected: category.key === selectedKey }}
|
|
82
|
+
aria-pressed={category.key === selectedKey}
|
|
79
83
|
style={[
|
|
80
84
|
styles.segment,
|
|
81
85
|
{
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React, { memo, useEffect, useState } from 'react';
|
|
2
2
|
import { View, type ViewStyle } from 'react-native';
|
|
3
3
|
|
|
4
|
+
import { adoptStyleSheet } from '../styles/adopt-style-sheet';
|
|
4
5
|
import type { WebCssStyle } from '../styles/web-view-style';
|
|
5
6
|
import { useTheme } from '../theme/use-theme';
|
|
6
7
|
import { ConnectionDotsBase } from './ConnectionDotsBase';
|
|
@@ -31,12 +32,7 @@ export const BLOOM_CONNECTION_DOTS_CSS = `
|
|
|
31
32
|
|
|
32
33
|
function useKeyframes(): void {
|
|
33
34
|
useEffect(() => {
|
|
34
|
-
|
|
35
|
-
if (document.getElementById(KEYFRAMES_ID)) return;
|
|
36
|
-
const style = document.createElement('style');
|
|
37
|
-
style.id = KEYFRAMES_ID;
|
|
38
|
-
style.textContent = BLOOM_CONNECTION_DOTS_CSS;
|
|
39
|
-
document.head.appendChild(style);
|
|
35
|
+
adoptStyleSheet(KEYFRAMES_ID, BLOOM_CONNECTION_DOTS_CSS);
|
|
40
36
|
}, []);
|
|
41
37
|
}
|
|
42
38
|
|
|
@@ -27,6 +27,22 @@ export type {
|
|
|
27
27
|
|
|
28
28
|
export { bloomThemeCss, bloomThemeBlock } from './theme-css';
|
|
29
29
|
|
|
30
|
+
/* The same tokens as a data structure rather than a stylesheet, for a consumer
|
|
31
|
+
* that is not a browser (`@oxyhq/bloom/design-tokens/tokens.json` is the
|
|
32
|
+
* generated artifact; these rebuild it in memory). Both come from the colour
|
|
33
|
+
* engine, so the JSON and the CSS cannot disagree. */
|
|
34
|
+
export {
|
|
35
|
+
bloomDesignTokens,
|
|
36
|
+
renderBloomTokensJson,
|
|
37
|
+
resolvedColorToHex,
|
|
38
|
+
} from './tokens-json';
|
|
39
|
+
export type {
|
|
40
|
+
BloomDesignTokens,
|
|
41
|
+
DesignToken,
|
|
42
|
+
PresetExtensions,
|
|
43
|
+
PresetGroup,
|
|
44
|
+
} from './tokens-json';
|
|
45
|
+
|
|
30
46
|
/* The resolved counterpart of `bloomThemeCss()`. That emits the alias layer
|
|
31
47
|
* (`--color-x: var(--x)`); these resolve `--x` itself, so a build script can
|
|
32
48
|
* write a static `:root` / `.dark` block and a prerendered page paints the real
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The machine-readable form of every Bloom design token, in W3C Design Tokens
|
|
3
|
+
* (DTCG) format.
|
|
4
|
+
*
|
|
5
|
+
* The CSS artifacts next to this file (`theme.css`, `bloomThemeCss()`) are the
|
|
6
|
+
* ALIAS layer and only make sense to a browser. A consumer that is not a
|
|
7
|
+
* browser — Astro codegens `SkColor` tables for Chromium's native UI so the
|
|
8
|
+
* toolbar and the WebUI pages cannot disagree about what "primary" is — needs
|
|
9
|
+
* the RESOLVED values, for every preset and both schemes, in a format it can
|
|
10
|
+
* read without running Bloom's colour engine. That is this file.
|
|
11
|
+
*
|
|
12
|
+
* Same single source as everything else: `getResolvedTokens`, i.e. the colour
|
|
13
|
+
* engine. `tokens.json` is generated from it by
|
|
14
|
+
* `scripts/generate-design-tokens-json.ts`, and
|
|
15
|
+
* `src/__tests__/design-tokens-json.test.ts` fails if the checked-in file is
|
|
16
|
+
* not byte-identical to a fresh render — so the JSON, the CSS and the runtime
|
|
17
|
+
* palette cannot drift from each other.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import {
|
|
21
|
+
APP_COLOR_NAMES,
|
|
22
|
+
APP_COLOR_PRESETS,
|
|
23
|
+
type AppColorName,
|
|
24
|
+
} from '../theme/color-presets';
|
|
25
|
+
import { getResolvedTokens } from '../theme/token-registry';
|
|
26
|
+
import {
|
|
27
|
+
BORDER_WIDTH,
|
|
28
|
+
RADIUS,
|
|
29
|
+
SPACING,
|
|
30
|
+
TYPOGRAPHY,
|
|
31
|
+
type TypeRoleName,
|
|
32
|
+
} from './scales';
|
|
33
|
+
|
|
34
|
+
/** One DTCG token. `$type` is inherited from the nearest group that declares it. */
|
|
35
|
+
export interface DesignToken {
|
|
36
|
+
$value: string | number;
|
|
37
|
+
$type?: string;
|
|
38
|
+
$description?: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Bloom-specific metadata on a preset group, namespaced per the DTCG `$extensions` rule. */
|
|
42
|
+
export interface PresetExtensions {
|
|
43
|
+
'so.oxy.bloom': {
|
|
44
|
+
/** The brand seed the engine derived the whole palette from. */
|
|
45
|
+
seed: string;
|
|
46
|
+
/** The tonal scheme variant used with that seed. */
|
|
47
|
+
variant: string;
|
|
48
|
+
/** Who may pick the preset; absent means everyone. */
|
|
49
|
+
gate?: string;
|
|
50
|
+
/** Present when the preset forces a white label in both schemes. */
|
|
51
|
+
label?: string;
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface PresetGroup {
|
|
56
|
+
$description: string;
|
|
57
|
+
$extensions: PresetExtensions;
|
|
58
|
+
light: Record<string, DesignToken>;
|
|
59
|
+
dark: Record<string, DesignToken>;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface BloomDesignTokens {
|
|
63
|
+
$description: string;
|
|
64
|
+
color: { $type: 'color'; $description: string } & Record<string, unknown>;
|
|
65
|
+
spacing: { $type: 'dimension'; $description: string } & Record<string, unknown>;
|
|
66
|
+
radius: { $type: 'dimension'; $description: string } & Record<string, unknown>;
|
|
67
|
+
borderWidth: { $type: 'dimension'; $description: string } & Record<string, unknown>;
|
|
68
|
+
typography: { $description: string } & Record<string, unknown>;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const FILE_DESCRIPTION = [
|
|
72
|
+
'Bloom design tokens, resolved. W3C Design Tokens Community Group format.',
|
|
73
|
+
'SHAPE CONTRACT (consumers codegen against this — additions are additive, existing paths do not move):',
|
|
74
|
+
'color.<preset>.<scheme>.<token> — <preset> is a Bloom AppColorName, <scheme> is "light" or "dark",',
|
|
75
|
+
'and <token> is the CSS custom property Bloom writes on the document root with its leading "--" removed,',
|
|
76
|
+
'so color.oxy.dark.card carries the value of --card under the oxy preset in dark.',
|
|
77
|
+
'Every colour $value is an sRGB hex string: "#rrggbb", or "#rrggbbaa" when the token carries alpha',
|
|
78
|
+
'(the -subtle family does). spacing/radius/borderWidth are DTCG dimensions with a px unit in the string.',
|
|
79
|
+
'typography.<role> is a group of fontSize (dimension), lineHeight (dimension) and fontWeight (number);',
|
|
80
|
+
'font FAMILIES are deliberately absent — they resolve to CSS variables a non-browser consumer cannot use.',
|
|
81
|
+
'Shadows are absent for the same reason: Bloom ships them as a platform-forked style object (bloomShadowStyle),',
|
|
82
|
+
'not as a value.',
|
|
83
|
+
'AUTO-GENERATED by scripts/generate-design-tokens-json.ts from Bloom’s colour engine — do not edit.',
|
|
84
|
+
].join(' ');
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Turn one resolved token value into an sRGB hex string.
|
|
88
|
+
*
|
|
89
|
+
* The engine emits exactly two forms — `rgb(r g b)` for opaque tokens and
|
|
90
|
+
* `rgba(r, g, b, a)` for the translucent `-subtle` family. Anything else is a
|
|
91
|
+
* change in the engine's output that this file has not been taught about, and
|
|
92
|
+
* throwing at generation time is the only way that surfaces: a silently
|
|
93
|
+
* passed-through CSS string would end up in a C++ colour table as garbage.
|
|
94
|
+
*/
|
|
95
|
+
export function resolvedColorToHex(value: string): string {
|
|
96
|
+
const channels = /^rgba?\(\s*([\d.]+)[\s,]+([\d.]+)[\s,]+([\d.]+)\s*(?:[,/]\s*([\d.]+%?)\s*)?\)$/.exec(
|
|
97
|
+
value,
|
|
98
|
+
);
|
|
99
|
+
if (!channels) {
|
|
100
|
+
throw new Error(
|
|
101
|
+
`Bloom token value is not an rgb()/rgba() colour: ${JSON.stringify(value)}`,
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const [, red = '0', green = '0', blue = '0', alpha] = channels;
|
|
106
|
+
const byte = (channel: string): string =>
|
|
107
|
+
Math.round(Number(channel)).toString(16).padStart(2, '0');
|
|
108
|
+
const hex = `#${byte(red)}${byte(green)}${byte(blue)}`;
|
|
109
|
+
|
|
110
|
+
if (alpha === undefined) return hex;
|
|
111
|
+
const fraction = alpha.endsWith('%')
|
|
112
|
+
? Number(alpha.slice(0, -1)) / 100
|
|
113
|
+
: Number(alpha);
|
|
114
|
+
if (fraction >= 1) return hex;
|
|
115
|
+
return `${hex}${byte(String(fraction * 255))}`;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function schemeTokens(
|
|
119
|
+
preset: AppColorName,
|
|
120
|
+
scheme: 'light' | 'dark',
|
|
121
|
+
): Record<string, DesignToken> {
|
|
122
|
+
const tokens: Record<string, DesignToken> = {};
|
|
123
|
+
for (const [property, value] of Object.entries(getResolvedTokens(preset, scheme))) {
|
|
124
|
+
tokens[property.replace(/^--/, '')] = { $value: resolvedColorToHex(value) };
|
|
125
|
+
}
|
|
126
|
+
return tokens;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function colorGroup(): BloomDesignTokens['color'] {
|
|
130
|
+
const group: Record<string, unknown> = {
|
|
131
|
+
$type: 'color',
|
|
132
|
+
$description:
|
|
133
|
+
'One group per Bloom colour preset, each with a light and a dark scheme. Token names match the CSS custom properties without the leading "--".',
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
for (const name of APP_COLOR_NAMES) {
|
|
137
|
+
const preset = APP_COLOR_PRESETS[name];
|
|
138
|
+
const extensions: PresetExtensions = {
|
|
139
|
+
'so.oxy.bloom': { seed: preset.hex, variant: preset.variant },
|
|
140
|
+
};
|
|
141
|
+
if (preset.gate) extensions['so.oxy.bloom'].gate = preset.gate;
|
|
142
|
+
if (preset.label) extensions['so.oxy.bloom'].label = preset.label;
|
|
143
|
+
|
|
144
|
+
group[name] = {
|
|
145
|
+
$description: `Derived by Bloom’s colour engine from seed ${preset.hex} (${preset.variant}).`,
|
|
146
|
+
$extensions: extensions,
|
|
147
|
+
light: schemeTokens(name, 'light'),
|
|
148
|
+
dark: schemeTokens(name, 'dark'),
|
|
149
|
+
} satisfies PresetGroup;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return group as BloomDesignTokens['color'];
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function dimensionGroup(
|
|
156
|
+
description: string,
|
|
157
|
+
scale: Record<string, number>,
|
|
158
|
+
): BloomDesignTokens['spacing'] {
|
|
159
|
+
const group: Record<string, unknown> = {
|
|
160
|
+
$type: 'dimension',
|
|
161
|
+
$description: description,
|
|
162
|
+
};
|
|
163
|
+
for (const [key, value] of Object.entries(scale)) {
|
|
164
|
+
group[key] = { $value: `${value}px` } satisfies DesignToken;
|
|
165
|
+
}
|
|
166
|
+
return group as BloomDesignTokens['spacing'];
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function typographyGroup(): BloomDesignTokens['typography'] {
|
|
170
|
+
const group: Record<string, unknown> = {
|
|
171
|
+
$description:
|
|
172
|
+
'One group per type role. Sizes and line-heights are px dimensions; weights are DTCG numeric font weights.',
|
|
173
|
+
};
|
|
174
|
+
for (const name of Object.keys(TYPOGRAPHY) as TypeRoleName[]) {
|
|
175
|
+
const role = TYPOGRAPHY[name];
|
|
176
|
+
group[name] = {
|
|
177
|
+
fontSize: { $type: 'dimension', $value: `${role.size}px` },
|
|
178
|
+
lineHeight: { $type: 'dimension', $value: `${role.lineHeight}px` },
|
|
179
|
+
fontWeight: { $type: 'fontWeight', $value: Number(role.weight) },
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
return group as BloomDesignTokens['typography'];
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/** The whole token file, as a plain object. */
|
|
186
|
+
export function bloomDesignTokens(): BloomDesignTokens {
|
|
187
|
+
return {
|
|
188
|
+
$description: FILE_DESCRIPTION,
|
|
189
|
+
color: colorGroup(),
|
|
190
|
+
spacing: dimensionGroup(
|
|
191
|
+
'The Oxy spacing scale. Same keys as the Tailwind/NativeWind utilities (p-space-8, gap-space-20, px-screen-margin).',
|
|
192
|
+
SPACING,
|
|
193
|
+
),
|
|
194
|
+
radius: dimensionGroup(
|
|
195
|
+
'The corner-radius scale. Same keys as the rounded-radius-* utilities; radius-max is the pill/circle sentinel.',
|
|
196
|
+
RADIUS,
|
|
197
|
+
),
|
|
198
|
+
borderWidth: dimensionGroup(
|
|
199
|
+
'Border widths. hairline is the 0.5px rule that pairs with the border-image colour role.',
|
|
200
|
+
BORDER_WIDTH,
|
|
201
|
+
),
|
|
202
|
+
typography: typographyGroup(),
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* The exact bytes of `design-tokens/tokens.json`.
|
|
208
|
+
*
|
|
209
|
+
* Both the generator script and the drift test call this, so neither can be
|
|
210
|
+
* right about the file while the other is wrong.
|
|
211
|
+
*/
|
|
212
|
+
export function renderBloomTokensJson(): string {
|
|
213
|
+
return `${JSON.stringify(bloomDesignTokens(), null, 2)}\n`;
|
|
214
|
+
}
|