@oxyhq/bloom 0.1.10 → 0.1.11
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/bottom-sheet/index.js +418 -0
- package/lib/commonjs/bottom-sheet/index.js.map +1 -0
- package/lib/commonjs/index.js +10 -1
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/theme/color-presets.js +1 -4
- package/lib/commonjs/theme/color-presets.js.map +1 -1
- package/lib/commonjs/theme/index.js +0 -6
- package/lib/commonjs/theme/index.js.map +1 -1
- package/lib/module/bottom-sheet/index.js +415 -0
- package/lib/module/bottom-sheet/index.js.map +1 -0
- package/lib/module/index.js +2 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/theme/color-presets.js +0 -3
- package/lib/module/theme/color-presets.js.map +1 -1
- package/lib/module/theme/index.js +1 -1
- package/lib/module/theme/index.js.map +1 -1
- package/lib/typescript/commonjs/bottom-sheet/index.d.ts +30 -0
- package/lib/typescript/commonjs/bottom-sheet/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/index.d.ts +2 -0
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/theme/color-presets.d.ts +0 -2
- package/lib/typescript/commonjs/theme/color-presets.d.ts.map +1 -1
- package/lib/typescript/commonjs/theme/index.d.ts +1 -1
- package/lib/typescript/commonjs/theme/index.d.ts.map +1 -1
- package/lib/typescript/module/bottom-sheet/index.d.ts +30 -0
- package/lib/typescript/module/bottom-sheet/index.d.ts.map +1 -0
- package/lib/typescript/module/index.d.ts +2 -0
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/theme/color-presets.d.ts +0 -2
- package/lib/typescript/module/theme/color-presets.d.ts.map +1 -1
- package/lib/typescript/module/theme/index.d.ts +1 -1
- package/lib/typescript/module/theme/index.d.ts.map +1 -1
- package/package.json +13 -1
- package/src/bottom-sheet/index.tsx +453 -0
- package/src/index.ts +4 -0
- package/src/theme/color-presets.ts +0 -3
- package/src/theme/index.ts +1 -1
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import { type ViewStyle, type StyleProp } from 'react-native';
|
|
3
|
+
export interface BottomSheetRef {
|
|
4
|
+
present: () => void;
|
|
5
|
+
dismiss: () => void;
|
|
6
|
+
close: () => void;
|
|
7
|
+
expand: () => void;
|
|
8
|
+
collapse: () => void;
|
|
9
|
+
scrollTo: (y: number, animated?: boolean) => void;
|
|
10
|
+
}
|
|
11
|
+
export interface BottomSheetProps {
|
|
12
|
+
children: React.ReactNode;
|
|
13
|
+
onDismiss?: () => void;
|
|
14
|
+
enablePanDownToClose?: boolean;
|
|
15
|
+
backgroundComponent?: (props: {
|
|
16
|
+
style?: StyleProp<ViewStyle>;
|
|
17
|
+
}) => React.ReactElement | null;
|
|
18
|
+
backdropComponent?: (props: {
|
|
19
|
+
style?: StyleProp<ViewStyle>;
|
|
20
|
+
onPress?: () => void;
|
|
21
|
+
}) => React.ReactElement | null;
|
|
22
|
+
style?: StyleProp<ViewStyle>;
|
|
23
|
+
enableHandlePanningGesture?: boolean;
|
|
24
|
+
onDismissAttempt?: () => boolean;
|
|
25
|
+
detached?: boolean;
|
|
26
|
+
}
|
|
27
|
+
declare const BottomSheet: React.ForwardRefExoticComponent<BottomSheetProps & React.RefAttributes<BottomSheetRef>>;
|
|
28
|
+
export default BottomSheet;
|
|
29
|
+
export { BottomSheet };
|
|
30
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/bottom-sheet/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,EAOH,KAAK,SAAS,EACd,KAAK,SAAS,EACjB,MAAM,cAAc,CAAC;AA+BtB,MAAM,WAAW,cAAc;IAC3B,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,QAAQ,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;CACrD;AAED,MAAM,WAAW,gBAAgB;IAC7B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,mBAAmB,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAA;KAAE,KAAK,KAAK,CAAC,YAAY,GAAG,IAAI,CAAC;IAC7F,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;KAAE,KAAK,KAAK,CAAC,YAAY,GAAG,IAAI,CAAC;IACjH,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC,gBAAgB,CAAC,EAAE,MAAM,OAAO,CAAC;IACjC,QAAQ,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,QAAA,MAAM,WAAW,yFA6Sf,CAAC;AAuFH,eAAe,WAAW,CAAC;AAC3B,OAAO,EAAE,WAAW,EAAE,CAAC"}
|
|
@@ -32,6 +32,8 @@ export { IconCircle } from './icon-circle';
|
|
|
32
32
|
export * as TextField from './text-field';
|
|
33
33
|
export * as SegmentedControl from './segmented-control';
|
|
34
34
|
export { SearchInput } from './search-input';
|
|
35
|
+
export { BottomSheet } from './bottom-sheet';
|
|
36
|
+
export type { BottomSheetRef, BottomSheetProps } from './bottom-sheet';
|
|
35
37
|
export * as Admonition from './admonition';
|
|
36
38
|
export * as Menu from './menu';
|
|
37
39
|
export * as Tooltip from './tooltip';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AACA,cAAc,SAAS,CAAC;AAGxB,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAC1C,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAC7D,OAAO,KAAK,MAAM,MAAM,iBAAiB,CAAC;AAC1C,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAGhF,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAG9D,OAAO,KAAK,KAAK,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,KAAK,KAAK,IAAI,SAAS,EAAE,KAAK,IAAI,SAAS,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAChG,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAG3E,cAAc,UAAU,CAAC;AACzB,OAAO,KAAK,MAAM,MAAM,UAAU,CAAC;AACnC,OAAO,KAAK,MAAM,MAAM,UAAU,CAAC;AACnC,cAAc,UAAU,CAAC;AACzB,cAAc,mBAAmB,CAAC;AAClC,cAAc,WAAW,CAAC;AAC1B,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,YAAY,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAC3D,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,OAAO,KAAK,WAAW,MAAM,gBAAgB,CAAC;AAC9C,cAAc,UAAU,CAAC;AACzB,OAAO,KAAK,KAAK,MAAM,SAAS,CAAC;AAGjC,OAAO,KAAK,UAAU,MAAM,cAAc,CAAC;AAG3C,OAAO,KAAK,QAAQ,MAAM,YAAY,CAAC;AACvC,OAAO,KAAK,IAAI,MAAM,QAAQ,CAAC;AAC/B,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAG3C,OAAO,KAAK,SAAS,MAAM,cAAc,CAAC;AAC1C,OAAO,KAAK,gBAAgB,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAG7C,OAAO,KAAK,UAAU,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,IAAI,MAAM,QAAQ,CAAC;AAC/B,OAAO,KAAK,OAAO,MAAM,WAAW,CAAC;AACrC,OAAO,KAAK,MAAM,MAAM,UAAU,CAAC;AACnC,OAAO,KAAK,WAAW,MAAM,gBAAgB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AACA,cAAc,SAAS,CAAC;AAGxB,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAC1C,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAC7D,OAAO,KAAK,MAAM,MAAM,iBAAiB,CAAC;AAC1C,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAGhF,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAG9D,OAAO,KAAK,KAAK,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,KAAK,KAAK,IAAI,SAAS,EAAE,KAAK,IAAI,SAAS,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAChG,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAG3E,cAAc,UAAU,CAAC;AACzB,OAAO,KAAK,MAAM,MAAM,UAAU,CAAC;AACnC,OAAO,KAAK,MAAM,MAAM,UAAU,CAAC;AACnC,cAAc,UAAU,CAAC;AACzB,cAAc,mBAAmB,CAAC;AAClC,cAAc,WAAW,CAAC;AAC1B,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,YAAY,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAC3D,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,OAAO,KAAK,WAAW,MAAM,gBAAgB,CAAC;AAC9C,cAAc,UAAU,CAAC;AACzB,OAAO,KAAK,KAAK,MAAM,SAAS,CAAC;AAGjC,OAAO,KAAK,UAAU,MAAM,cAAc,CAAC;AAG3C,OAAO,KAAK,QAAQ,MAAM,YAAY,CAAC;AACvC,OAAO,KAAK,IAAI,MAAM,QAAQ,CAAC;AAC/B,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAG3C,OAAO,KAAK,SAAS,MAAM,cAAc,CAAC;AAC1C,OAAO,KAAK,gBAAgB,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAG7C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,YAAY,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAGvE,OAAO,KAAK,UAAU,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,IAAI,MAAM,QAAQ,CAAC;AAC/B,OAAO,KAAK,OAAO,MAAM,WAAW,CAAC;AACrC,OAAO,KAAK,MAAM,MAAM,UAAU,CAAC;AACnC,OAAO,KAAK,WAAW,MAAM,gBAAgB,CAAC"}
|
|
@@ -6,8 +6,6 @@ export interface AppColorPreset {
|
|
|
6
6
|
dark: Record<string, string>;
|
|
7
7
|
}
|
|
8
8
|
export declare const APP_COLOR_NAMES: AppColorName[];
|
|
9
|
-
/** Premium-exclusive color names, not shown in the standard picker */
|
|
10
|
-
export declare const PREMIUM_COLOR_NAMES: AppColorName[];
|
|
11
9
|
export declare const HEX_TO_APP_COLOR: Record<string, AppColorName>;
|
|
12
10
|
export declare function hexToAppColorName(hex: string): AppColorName;
|
|
13
11
|
export declare const APP_COLOR_PRESETS: Record<AppColorName, AppColorPreset>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"color-presets.d.ts","sourceRoot":"","sources":["../../../../src/theme/color-presets.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,KAAK,CAAC;AAE/H,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,YAAY,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9B;AAED,eAAO,MAAM,eAAe,EAAE,YAAY,EAAyF,CAAC;AAEpI,
|
|
1
|
+
{"version":3,"file":"color-presets.d.ts","sourceRoot":"","sources":["../../../../src/theme/color-presets.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,KAAK,CAAC;AAE/H,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,YAAY,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9B;AAED,eAAO,MAAM,eAAe,EAAE,YAAY,EAAyF,CAAC;AAEpI,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAYzD,CAAC;AAEF,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,YAAY,CAE3D;AAED,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,YAAY,EAAE,cAAc,CAqgBlE,CAAC"}
|
|
@@ -3,7 +3,7 @@ export type { BloomThemeProviderProps, BloomThemeContextValue } from './BloomThe
|
|
|
3
3
|
export { useTheme, useThemeColor, useBloomTheme } from './use-theme';
|
|
4
4
|
export type { Theme, ThemeColors, ThemeMode } from './types';
|
|
5
5
|
export type { AppColorName, AppColorPreset } from './color-presets';
|
|
6
|
-
export { APP_COLOR_NAMES, APP_COLOR_PRESETS,
|
|
6
|
+
export { APP_COLOR_NAMES, APP_COLOR_PRESETS, HEX_TO_APP_COLOR, hexToAppColorName } from './color-presets';
|
|
7
7
|
export { applyDarkClass } from './apply-dark-class';
|
|
8
8
|
export { setColorSchemeSafe } from './set-color-scheme-safe';
|
|
9
9
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/theme/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,YAAY,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC5F,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACrE,YAAY,EAAE,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAC7D,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/theme/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,YAAY,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC5F,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACrE,YAAY,EAAE,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAC7D,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAC1G,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oxyhq/bloom",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.11",
|
|
4
4
|
"description": "Bloom UI — Oxy ecosystem component library for React Native + Expo + Web",
|
|
5
5
|
"main": "lib/commonjs/index.js",
|
|
6
6
|
"module": "lib/module/index.js",
|
|
@@ -364,6 +364,17 @@
|
|
|
364
364
|
"default": "./lib/commonjs/select/index.js"
|
|
365
365
|
}
|
|
366
366
|
},
|
|
367
|
+
"./bottom-sheet": {
|
|
368
|
+
"react-native": "./src/bottom-sheet/index.tsx",
|
|
369
|
+
"import": {
|
|
370
|
+
"types": "./lib/typescript/module/bottom-sheet/index.d.ts",
|
|
371
|
+
"default": "./lib/module/bottom-sheet/index.js"
|
|
372
|
+
},
|
|
373
|
+
"require": {
|
|
374
|
+
"types": "./lib/typescript/commonjs/bottom-sheet/index.d.ts",
|
|
375
|
+
"default": "./lib/commonjs/bottom-sheet/index.js"
|
|
376
|
+
}
|
|
377
|
+
},
|
|
367
378
|
"./context-menu": {
|
|
368
379
|
"react-native": "./src/context-menu/index.tsx",
|
|
369
380
|
"import": {
|
|
@@ -409,6 +420,7 @@
|
|
|
409
420
|
"tooltip",
|
|
410
421
|
"select",
|
|
411
422
|
"context-menu",
|
|
423
|
+
"bottom-sheet",
|
|
412
424
|
"ai"
|
|
413
425
|
],
|
|
414
426
|
"repository": {
|
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import { forwardRef, useImperativeHandle, useRef, useEffect, useState, useCallback, useMemo } from 'react';
|
|
3
|
+
import {
|
|
4
|
+
View,
|
|
5
|
+
StyleSheet,
|
|
6
|
+
Modal,
|
|
7
|
+
Pressable,
|
|
8
|
+
Dimensions,
|
|
9
|
+
Platform,
|
|
10
|
+
type ViewStyle,
|
|
11
|
+
type StyleProp,
|
|
12
|
+
} from 'react-native';
|
|
13
|
+
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
|
|
14
|
+
import Animated, {
|
|
15
|
+
interpolate,
|
|
16
|
+
runOnJS,
|
|
17
|
+
useAnimatedScrollHandler,
|
|
18
|
+
useAnimatedStyle,
|
|
19
|
+
useSharedValue,
|
|
20
|
+
withSpring,
|
|
21
|
+
withTiming,
|
|
22
|
+
} from 'react-native-reanimated';
|
|
23
|
+
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
24
|
+
import { useTheme } from '../theme/use-theme';
|
|
25
|
+
|
|
26
|
+
// Optional dependency — keyboard handling is skipped if not installed
|
|
27
|
+
let useKeyboardHandler: ((handlers: Record<string, (e: { height: number }) => void>, deps: unknown[]) => void) | undefined;
|
|
28
|
+
try {
|
|
29
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
30
|
+
useKeyboardHandler = require('react-native-keyboard-controller').useKeyboardHandler;
|
|
31
|
+
} catch {
|
|
32
|
+
// react-native-keyboard-controller not available
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const { width: SCREEN_WIDTH, height: SCREEN_HEIGHT } = Dimensions.get('window');
|
|
36
|
+
|
|
37
|
+
const SPRING_CONFIG = {
|
|
38
|
+
damping: 25,
|
|
39
|
+
stiffness: 300,
|
|
40
|
+
mass: 0.8,
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export interface BottomSheetRef {
|
|
44
|
+
present: () => void;
|
|
45
|
+
dismiss: () => void;
|
|
46
|
+
close: () => void;
|
|
47
|
+
expand: () => void;
|
|
48
|
+
collapse: () => void;
|
|
49
|
+
scrollTo: (y: number, animated?: boolean) => void;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface BottomSheetProps {
|
|
53
|
+
children: React.ReactNode;
|
|
54
|
+
onDismiss?: () => void;
|
|
55
|
+
enablePanDownToClose?: boolean;
|
|
56
|
+
backgroundComponent?: (props: { style?: StyleProp<ViewStyle> }) => React.ReactElement | null;
|
|
57
|
+
backdropComponent?: (props: { style?: StyleProp<ViewStyle>; onPress?: () => void }) => React.ReactElement | null;
|
|
58
|
+
style?: StyleProp<ViewStyle>;
|
|
59
|
+
enableHandlePanningGesture?: boolean;
|
|
60
|
+
onDismissAttempt?: () => boolean;
|
|
61
|
+
detached?: boolean; // If true, shows with margins and rounded corners. If false, full width with rounded top only.
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const BottomSheet = forwardRef((props: BottomSheetProps, ref: React.ForwardedRef<BottomSheetRef>) => {
|
|
65
|
+
const {
|
|
66
|
+
children,
|
|
67
|
+
onDismiss,
|
|
68
|
+
enablePanDownToClose = true,
|
|
69
|
+
backgroundComponent,
|
|
70
|
+
backdropComponent,
|
|
71
|
+
style,
|
|
72
|
+
enableHandlePanningGesture = true,
|
|
73
|
+
onDismissAttempt,
|
|
74
|
+
detached = false,
|
|
75
|
+
} = props;
|
|
76
|
+
|
|
77
|
+
const insets = useSafeAreaInsets();
|
|
78
|
+
const { colors } = useTheme();
|
|
79
|
+
const [visible, setVisible] = useState(false);
|
|
80
|
+
const [rendered, setRendered] = useState(false); // keep mounted for exit animation
|
|
81
|
+
const closeTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
82
|
+
const hasClosedRef = useRef(false);
|
|
83
|
+
const scrollViewRef = useRef<Animated.ScrollView>(null);
|
|
84
|
+
|
|
85
|
+
const translateY = useSharedValue(SCREEN_HEIGHT);
|
|
86
|
+
const opacity = useSharedValue(0);
|
|
87
|
+
const scrollOffsetY = useSharedValue(0);
|
|
88
|
+
const isScrollAtTop = useSharedValue(true);
|
|
89
|
+
const allowPanClose = useSharedValue(true);
|
|
90
|
+
const keyboardHeight = useSharedValue(0);
|
|
91
|
+
const context = useSharedValue({ y: 0 });
|
|
92
|
+
|
|
93
|
+
if (useKeyboardHandler) {
|
|
94
|
+
useKeyboardHandler({
|
|
95
|
+
onMove: (e) => {
|
|
96
|
+
'worklet';
|
|
97
|
+
keyboardHeight.value = e.height;
|
|
98
|
+
},
|
|
99
|
+
onEnd: (e) => {
|
|
100
|
+
'worklet';
|
|
101
|
+
keyboardHeight.value = e.height;
|
|
102
|
+
},
|
|
103
|
+
}, []);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Dismiss callbacks
|
|
107
|
+
const safeClose = () => {
|
|
108
|
+
if (onDismissAttempt?.()) {
|
|
109
|
+
onDismiss?.();
|
|
110
|
+
} else if (!onDismissAttempt) {
|
|
111
|
+
onDismiss?.();
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const finishClose = useCallback(() => {
|
|
116
|
+
if (hasClosedRef.current) return;
|
|
117
|
+
hasClosedRef.current = true;
|
|
118
|
+
safeClose();
|
|
119
|
+
setRendered(false);
|
|
120
|
+
}, [safeClose]);
|
|
121
|
+
|
|
122
|
+
useEffect(() => {
|
|
123
|
+
if (visible) {
|
|
124
|
+
if (closeTimeoutRef.current) {
|
|
125
|
+
clearTimeout(closeTimeoutRef.current);
|
|
126
|
+
closeTimeoutRef.current = null;
|
|
127
|
+
}
|
|
128
|
+
hasClosedRef.current = false;
|
|
129
|
+
opacity.value = withTiming(1, { duration: 250 });
|
|
130
|
+
translateY.value = withSpring(0, SPRING_CONFIG);
|
|
131
|
+
} else if (rendered) {
|
|
132
|
+
opacity.value = withTiming(0, { duration: 250 }, (finished) => {
|
|
133
|
+
if (finished) {
|
|
134
|
+
runOnJS(finishClose)();
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
translateY.value = withSpring(SCREEN_HEIGHT, { ...SPRING_CONFIG, stiffness: 250 });
|
|
138
|
+
|
|
139
|
+
// Fallback timer to ensure close completes (especially on web)
|
|
140
|
+
if (closeTimeoutRef.current) {
|
|
141
|
+
clearTimeout(closeTimeoutRef.current);
|
|
142
|
+
}
|
|
143
|
+
closeTimeoutRef.current = setTimeout(() => {
|
|
144
|
+
finishClose();
|
|
145
|
+
closeTimeoutRef.current = null;
|
|
146
|
+
}, 300);
|
|
147
|
+
}
|
|
148
|
+
}, [visible, rendered, finishClose]);
|
|
149
|
+
|
|
150
|
+
// Clear pending timeout on unmount
|
|
151
|
+
useEffect(() => () => {
|
|
152
|
+
if (closeTimeoutRef.current) {
|
|
153
|
+
clearTimeout(closeTimeoutRef.current);
|
|
154
|
+
closeTimeoutRef.current = null;
|
|
155
|
+
}
|
|
156
|
+
}, []);
|
|
157
|
+
|
|
158
|
+
// Apply web scrollbar styles when colors change
|
|
159
|
+
useEffect(() => {
|
|
160
|
+
if (Platform.OS === 'web') {
|
|
161
|
+
createWebScrollbarStyle(colors.border);
|
|
162
|
+
}
|
|
163
|
+
}, [colors.border]);
|
|
164
|
+
|
|
165
|
+
const present = useCallback(() => {
|
|
166
|
+
setRendered(true);
|
|
167
|
+
setVisible(true);
|
|
168
|
+
}, []);
|
|
169
|
+
const dismiss = useCallback(() => {
|
|
170
|
+
setVisible(false);
|
|
171
|
+
}, []);
|
|
172
|
+
|
|
173
|
+
const scrollTo = useCallback((y: number, animated = true) => {
|
|
174
|
+
scrollViewRef.current?.scrollTo({ y, animated });
|
|
175
|
+
}, []);
|
|
176
|
+
|
|
177
|
+
useImperativeHandle(ref, () => ({
|
|
178
|
+
present,
|
|
179
|
+
dismiss,
|
|
180
|
+
close: dismiss,
|
|
181
|
+
expand: present,
|
|
182
|
+
collapse: dismiss,
|
|
183
|
+
scrollTo,
|
|
184
|
+
}), [present, dismiss, scrollTo]);
|
|
185
|
+
|
|
186
|
+
const nativeGesture = useMemo(() => Gesture.Native(), []);
|
|
187
|
+
|
|
188
|
+
const panGesture = Gesture.Pan()
|
|
189
|
+
.enabled(enablePanDownToClose)
|
|
190
|
+
.simultaneousWithExternalGesture(nativeGesture)
|
|
191
|
+
.onStart(() => {
|
|
192
|
+
'worklet';
|
|
193
|
+
context.value = { y: translateY.value };
|
|
194
|
+
allowPanClose.value = scrollOffsetY.value <= 8;
|
|
195
|
+
})
|
|
196
|
+
.onUpdate((event) => {
|
|
197
|
+
'worklet';
|
|
198
|
+
if (!allowPanClose.value) {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
const newTranslateY = context.value.y + event.translationY;
|
|
202
|
+
// If user is scrolling down while content isn't at (or near) the top, let ScrollView handle it
|
|
203
|
+
const atTopOrNearTop = scrollOffsetY.value <= 8; // slightly larger tolerance for smoother handoff
|
|
204
|
+
if (event.translationY > 0 && !atTopOrNearTop) {
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
if (newTranslateY >= 0) {
|
|
208
|
+
translateY.value = newTranslateY;
|
|
209
|
+
} else if (detached) {
|
|
210
|
+
// Only allow overdrag (pulling up beyond top) when detached
|
|
211
|
+
translateY.value = newTranslateY * 0.3;
|
|
212
|
+
} else {
|
|
213
|
+
// In normal mode, prevent overdrag - clamp to 0
|
|
214
|
+
translateY.value = 0;
|
|
215
|
+
}
|
|
216
|
+
})
|
|
217
|
+
.onEnd((event) => {
|
|
218
|
+
'worklet';
|
|
219
|
+
if (!allowPanClose.value) {
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
const velocity = event.velocityY;
|
|
223
|
+
const distance = translateY.value;
|
|
224
|
+
// Require a deeper pull to close (more like native bottom sheets)
|
|
225
|
+
const closeThreshold = Math.max(140, SCREEN_HEIGHT * 0.25);
|
|
226
|
+
const fastSwipeThreshold = 900;
|
|
227
|
+
const shouldClose =
|
|
228
|
+
velocity > fastSwipeThreshold ||
|
|
229
|
+
(distance > closeThreshold && velocity > -300);
|
|
230
|
+
|
|
231
|
+
if (shouldClose) {
|
|
232
|
+
translateY.value = withSpring(SCREEN_HEIGHT, {
|
|
233
|
+
...SPRING_CONFIG,
|
|
234
|
+
velocity: velocity,
|
|
235
|
+
});
|
|
236
|
+
opacity.value = withTiming(0, { duration: 250 }, (finished) => {
|
|
237
|
+
if (finished) {
|
|
238
|
+
runOnJS(finishClose)();
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
} else {
|
|
242
|
+
translateY.value = withSpring(0, {
|
|
243
|
+
...SPRING_CONFIG,
|
|
244
|
+
velocity: velocity,
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
const backdropStyle = useAnimatedStyle(() => ({
|
|
250
|
+
opacity: opacity.value,
|
|
251
|
+
}));
|
|
252
|
+
|
|
253
|
+
const sheetStyle = useAnimatedStyle(() => {
|
|
254
|
+
const scale = interpolate(translateY.value, [0, SCREEN_HEIGHT], [1, 0.95]);
|
|
255
|
+
return {
|
|
256
|
+
transform: [
|
|
257
|
+
{ translateY: translateY.value - keyboardHeight.value },
|
|
258
|
+
{ scale },
|
|
259
|
+
],
|
|
260
|
+
};
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
const sheetHeightStyle = useAnimatedStyle(() => ({
|
|
264
|
+
maxHeight: SCREEN_HEIGHT - keyboardHeight.value - insets.top - (detached ? insets.bottom + 16 : 0),
|
|
265
|
+
}), [insets.top, insets.bottom, detached]);
|
|
266
|
+
|
|
267
|
+
const sheetMarginStyle = useAnimatedStyle(() => {
|
|
268
|
+
// Only add margin when detached, otherwise extend behind safe area
|
|
269
|
+
if (detached) {
|
|
270
|
+
return {
|
|
271
|
+
marginBottom: keyboardHeight.value > 0 ? 16 : insets.bottom + 16,
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
return {
|
|
275
|
+
marginBottom: 0,
|
|
276
|
+
};
|
|
277
|
+
}, [insets.bottom, detached]);
|
|
278
|
+
|
|
279
|
+
const handleBackdropPress = useCallback(() => {
|
|
280
|
+
// Always animate close on backdrop press
|
|
281
|
+
if (onDismissAttempt && !onDismissAttempt()) {
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
dismiss();
|
|
285
|
+
}, [onDismissAttempt, dismiss]);
|
|
286
|
+
|
|
287
|
+
const scrollHandler = useAnimatedScrollHandler({
|
|
288
|
+
onScroll: (event) => {
|
|
289
|
+
scrollOffsetY.value = event.contentOffset.y;
|
|
290
|
+
isScrollAtTop.value = event.contentOffset.y <= 0;
|
|
291
|
+
},
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
const dynamicStyles = useMemo(() => {
|
|
295
|
+
const isDark = colors.background === '#000000';
|
|
296
|
+
return StyleSheet.create({
|
|
297
|
+
handle: {
|
|
298
|
+
...styles.handle,
|
|
299
|
+
backgroundColor: isDark ? '#444' : '#C7C7CC',
|
|
300
|
+
},
|
|
301
|
+
sheet: {
|
|
302
|
+
...styles.sheet,
|
|
303
|
+
backgroundColor: colors.background,
|
|
304
|
+
...(detached ? styles.sheetDetached : styles.sheetNormal),
|
|
305
|
+
},
|
|
306
|
+
scrollContent: {
|
|
307
|
+
...styles.scrollContent,
|
|
308
|
+
// In normal mode, don't add padding here - screens handle their own padding
|
|
309
|
+
// The sheet extends behind safe area, and screens add padding as needed
|
|
310
|
+
},
|
|
311
|
+
});
|
|
312
|
+
}, [colors.background, detached, insets.bottom]);
|
|
313
|
+
|
|
314
|
+
if (!rendered) return null;
|
|
315
|
+
|
|
316
|
+
return (
|
|
317
|
+
<Modal visible={rendered} transparent animationType="none" statusBarTranslucent onRequestClose={dismiss}>
|
|
318
|
+
<View style={StyleSheet.absoluteFill}>
|
|
319
|
+
<Animated.View style={[styles.backdrop, backdropStyle]}>
|
|
320
|
+
{backdropComponent ? (
|
|
321
|
+
backdropComponent({ onPress: handleBackdropPress })
|
|
322
|
+
) : (
|
|
323
|
+
<Pressable style={styles.backdropTouchable} onPress={handleBackdropPress}>
|
|
324
|
+
<View style={StyleSheet.absoluteFill} />
|
|
325
|
+
</Pressable>
|
|
326
|
+
)}
|
|
327
|
+
</Animated.View>
|
|
328
|
+
|
|
329
|
+
<GestureDetector gesture={panGesture}>
|
|
330
|
+
<Animated.View style={[dynamicStyles.sheet, sheetMarginStyle, sheetStyle, sheetHeightStyle]}>
|
|
331
|
+
{backgroundComponent?.({ style: styles.background })}
|
|
332
|
+
|
|
333
|
+
<View style={dynamicStyles.handle} />
|
|
334
|
+
|
|
335
|
+
<GestureDetector gesture={nativeGesture}>
|
|
336
|
+
<Animated.ScrollView
|
|
337
|
+
ref={scrollViewRef}
|
|
338
|
+
style={[
|
|
339
|
+
styles.scrollView,
|
|
340
|
+
Platform.OS === 'web' && ({
|
|
341
|
+
scrollbarWidth: 'thin',
|
|
342
|
+
scrollbarColor: `${colors.border} transparent`,
|
|
343
|
+
} as ViewStyle),
|
|
344
|
+
]}
|
|
345
|
+
contentContainerStyle={dynamicStyles.scrollContent}
|
|
346
|
+
showsVerticalScrollIndicator={false}
|
|
347
|
+
keyboardShouldPersistTaps="handled"
|
|
348
|
+
onScroll={scrollHandler}
|
|
349
|
+
scrollEventThrottle={16}
|
|
350
|
+
{...(Platform.OS === 'web' ? { className: 'bottom-sheet-scrollview' } : undefined)}
|
|
351
|
+
onLayout={() => {
|
|
352
|
+
if (Platform.OS === 'web') {
|
|
353
|
+
createWebScrollbarStyle(colors.border);
|
|
354
|
+
}
|
|
355
|
+
}}
|
|
356
|
+
>
|
|
357
|
+
{children}
|
|
358
|
+
</Animated.ScrollView>
|
|
359
|
+
</GestureDetector>
|
|
360
|
+
</Animated.View>
|
|
361
|
+
</GestureDetector>
|
|
362
|
+
</View>
|
|
363
|
+
</Modal>
|
|
364
|
+
);
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
BottomSheet.displayName = 'BottomSheet';
|
|
368
|
+
|
|
369
|
+
const styles = StyleSheet.create({
|
|
370
|
+
backdrop: {
|
|
371
|
+
flex: 1,
|
|
372
|
+
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
|
373
|
+
},
|
|
374
|
+
backdropTouchable: {
|
|
375
|
+
flex: 1,
|
|
376
|
+
},
|
|
377
|
+
sheet: {
|
|
378
|
+
position: 'absolute',
|
|
379
|
+
bottom: 0,
|
|
380
|
+
overflow: 'hidden',
|
|
381
|
+
maxWidth: 800,
|
|
382
|
+
alignSelf: 'center',
|
|
383
|
+
marginHorizontal: 'auto',
|
|
384
|
+
},
|
|
385
|
+
sheetDetached: {
|
|
386
|
+
left: 16,
|
|
387
|
+
right: 16,
|
|
388
|
+
borderRadius: 24,
|
|
389
|
+
},
|
|
390
|
+
sheetNormal: {
|
|
391
|
+
left: 0,
|
|
392
|
+
right: 0,
|
|
393
|
+
borderTopLeftRadius: 24,
|
|
394
|
+
borderTopRightRadius: 24,
|
|
395
|
+
},
|
|
396
|
+
handle: {
|
|
397
|
+
position: 'absolute',
|
|
398
|
+
top: 10,
|
|
399
|
+
left: '50%',
|
|
400
|
+
marginLeft: -18,
|
|
401
|
+
width: 36,
|
|
402
|
+
height: 5,
|
|
403
|
+
borderRadius: 3,
|
|
404
|
+
zIndex: 100,
|
|
405
|
+
},
|
|
406
|
+
background: {
|
|
407
|
+
...StyleSheet.absoluteFillObject,
|
|
408
|
+
},
|
|
409
|
+
scrollView: {
|
|
410
|
+
flex: 1,
|
|
411
|
+
},
|
|
412
|
+
scrollContent: {
|
|
413
|
+
flexGrow: 1,
|
|
414
|
+
},
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
// Create web scrollbar styles dynamically based on theme
|
|
418
|
+
const createWebScrollbarStyle = (borderColor: string) => {
|
|
419
|
+
if (Platform.OS !== 'web' || typeof document === 'undefined') return;
|
|
420
|
+
|
|
421
|
+
const styleId = 'bottom-sheet-scrollbar-style';
|
|
422
|
+
let styleElement = document.getElementById(styleId) as HTMLStyleElement;
|
|
423
|
+
|
|
424
|
+
if (!styleElement) {
|
|
425
|
+
styleElement = document.createElement('style');
|
|
426
|
+
styleElement.id = styleId;
|
|
427
|
+
document.head.appendChild(styleElement);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// Use theme border color for scrollbar
|
|
431
|
+
const scrollbarColor = borderColor;
|
|
432
|
+
const scrollbarHoverColor = borderColor === '#E5E5EA' ? '#C7C7CC' : '#555';
|
|
433
|
+
|
|
434
|
+
styleElement.textContent = `
|
|
435
|
+
.bottom-sheet-scrollview::-webkit-scrollbar {
|
|
436
|
+
width: 6px;
|
|
437
|
+
}
|
|
438
|
+
.bottom-sheet-scrollview::-webkit-scrollbar-track {
|
|
439
|
+
background: transparent;
|
|
440
|
+
border-radius: 10px;
|
|
441
|
+
}
|
|
442
|
+
.bottom-sheet-scrollview::-webkit-scrollbar-thumb {
|
|
443
|
+
background: ${scrollbarColor};
|
|
444
|
+
border-radius: 10px;
|
|
445
|
+
}
|
|
446
|
+
.bottom-sheet-scrollview::-webkit-scrollbar-thumb:hover {
|
|
447
|
+
background: ${scrollbarHoverColor};
|
|
448
|
+
}
|
|
449
|
+
`;
|
|
450
|
+
};
|
|
451
|
+
|
|
452
|
+
export default BottomSheet;
|
|
453
|
+
export { BottomSheet };
|
package/src/index.ts
CHANGED
|
@@ -48,6 +48,10 @@ export * as TextField from './text-field';
|
|
|
48
48
|
export * as SegmentedControl from './segmented-control';
|
|
49
49
|
export { SearchInput } from './search-input';
|
|
50
50
|
|
|
51
|
+
// Bottom sheet
|
|
52
|
+
export { BottomSheet } from './bottom-sheet';
|
|
53
|
+
export type { BottomSheetRef, BottomSheetProps } from './bottom-sheet';
|
|
54
|
+
|
|
51
55
|
// Overlay components
|
|
52
56
|
export * as Admonition from './admonition';
|
|
53
57
|
export * as Menu from './menu';
|
|
@@ -9,9 +9,6 @@ export interface AppColorPreset {
|
|
|
9
9
|
|
|
10
10
|
export const APP_COLOR_NAMES: AppColorName[] = ['teal', 'blue', 'green', 'amber', 'red', 'purple', 'pink', 'sky', 'orange', 'mint'];
|
|
11
11
|
|
|
12
|
-
/** Premium-exclusive color names, not shown in the standard picker */
|
|
13
|
-
export const PREMIUM_COLOR_NAMES: AppColorName[] = ['oxy'];
|
|
14
|
-
|
|
15
12
|
export const HEX_TO_APP_COLOR: Record<string, AppColorName> = {
|
|
16
13
|
'#005c67': 'teal',
|
|
17
14
|
'#1d9bf0': 'blue',
|
package/src/theme/index.ts
CHANGED
|
@@ -3,6 +3,6 @@ export type { BloomThemeProviderProps, BloomThemeContextValue } from './BloomThe
|
|
|
3
3
|
export { useTheme, useThemeColor, useBloomTheme } from './use-theme';
|
|
4
4
|
export type { Theme, ThemeColors, ThemeMode } from './types';
|
|
5
5
|
export type { AppColorName, AppColorPreset } from './color-presets';
|
|
6
|
-
export { APP_COLOR_NAMES, APP_COLOR_PRESETS,
|
|
6
|
+
export { APP_COLOR_NAMES, APP_COLOR_PRESETS, HEX_TO_APP_COLOR, hexToAppColorName } from './color-presets';
|
|
7
7
|
export { applyDarkClass } from './apply-dark-class';
|
|
8
8
|
export { setColorSchemeSafe } from './set-color-scheme-safe';
|