@rific/auto-paper 0.13.0 → 0.13.2
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/README.md +3 -2
- package/dist/index.d.mts +12 -7
- package/dist/index.d.ts +12 -7
- package/dist/index.js +208 -205
- package/dist/index.mjs +190 -187
- package/package.json +5 -5
- package/src/ThemeProvider.tsx +30 -19
- package/src/components/Appbar.tsx +4 -1
- package/src/components/FadingScrollView.tsx +4 -3
package/README.md
CHANGED
|
@@ -272,7 +272,8 @@ export default function App() {
|
|
|
272
272
|
| `onNavBarChange` | `(color: string, dark: boolean) => void` | Overrides the built-in nav bar sync: called on Android when the theme changes while a `BottomNavigation` is mounted, instead of the automatic `navigationBar`-driven icon-style sync |
|
|
273
273
|
| `onReady` | `() => void` | Called once when the theme first resolves |
|
|
274
274
|
| `reanimated` | `ReanimatedModule` | Injects `react-native-reanimated` (for example `import Reanimated from 'react-native-reanimated'`) so `<Dialog animatedStyle={...}>` can animate its card on the UI thread. Omit to keep `animatedStyle` as a no-op. |
|
|
275
|
-
| `
|
|
275
|
+
| `statusBar` | `boolean` | Renders React Native's `StatusBar`, styled from the theme (here and in `Appbar.Header`). Default `true`. Pass `false` when the app sets the status bar per screen instead (react-native-screens' `statusBarStyle`/`statusBarHidden` screen options, which need `UIViewControllerBasedStatusBarAppearance` on): there a React Native `StatusBar` logs an error. An app linked against the iOS 27 SDK can't set the status bar app-wide at all, which is the only way that component works on iOS, so per-screen options are the way to style it there. |
|
|
276
|
+
| `statusBarProps` | `StatusBarProps` | Spread over the auto-derived `StatusBar` defaults (ignored when `statusBar` is `false`) |
|
|
276
277
|
| `style` | `StyleProp<ViewStyle>` | Applied to the wrapper `View` |
|
|
277
278
|
|
|
278
279
|
`Provider` also renders its own `Portal.Host`, nested inside everything it injects, so anything rendered through `react-native-paper`'s `Portal` (this package's `Dialog`, Paper's `Menu` and `Tooltip`, or a `Modal`/`Snackbar`/anything else you wrap in `<Portal>` yourself) still sees `expoBlur`, `reanimated` and `defaults`. Paper's own host sits above all of those and its `Portal` only carries the theme across, so without this a portaled `Dialog` or `Menu` would silently render its solid fallback. Contexts your app provides *inside* `Provider` (a toast provider, say) are a separate matter: portal content can't see them unless you render another `<Portal.Host>` below them.
|
|
@@ -412,7 +413,7 @@ function SettingsScreen() {
|
|
|
412
413
|
|
|
413
414
|
### `Appbar`
|
|
414
415
|
|
|
415
|
-
A thin wrapper around `react-native-paper`'s `Appbar` that automatically syncs `StatusBar` background color and bar style to the current theme surface color.
|
|
416
|
+
A thin wrapper around `react-native-paper`'s `Appbar` that automatically syncs `StatusBar` background color and bar style to the current theme surface color (no `StatusBar` under `<Provider statusBar={false}>`).
|
|
416
417
|
|
|
417
418
|
```tsx
|
|
418
419
|
import { Appbar } from '@rific/auto-paper'
|
package/dist/index.d.mts
CHANGED
|
@@ -257,19 +257,19 @@ declare const themeActions: {
|
|
|
257
257
|
type: string;
|
|
258
258
|
}) => action is _rific_core.SettingsAction<string | TriadicPalette>;
|
|
259
259
|
};
|
|
260
|
-
|
|
261
|
-
(payload:
|
|
260
|
+
setBlur: {
|
|
261
|
+
(payload: boolean): _rific_core.SettingsAction<boolean>;
|
|
262
262
|
type: string;
|
|
263
263
|
match: (action: {
|
|
264
264
|
type: string;
|
|
265
|
-
}) => action is _rific_core.SettingsAction<
|
|
265
|
+
}) => action is _rific_core.SettingsAction<boolean>;
|
|
266
266
|
};
|
|
267
|
-
|
|
268
|
-
(payload:
|
|
267
|
+
setAppearance: {
|
|
268
|
+
(payload: ThemeAppearance): _rific_core.SettingsAction<ThemeAppearance>;
|
|
269
269
|
type: string;
|
|
270
270
|
match: (action: {
|
|
271
271
|
type: string;
|
|
272
|
-
}) => action is _rific_core.SettingsAction<
|
|
272
|
+
}) => action is _rific_core.SettingsAction<ThemeAppearance>;
|
|
273
273
|
};
|
|
274
274
|
setHarmony: {
|
|
275
275
|
(payload: ColorHarmony): _rific_core.SettingsAction<ColorHarmony>;
|
|
@@ -326,10 +326,15 @@ type ProviderProps = {
|
|
|
326
326
|
onReady?: () => void;
|
|
327
327
|
/** Injects react-native-reanimated so `<Dialog animatedStyle={...}>` (e.g. from `useAnimatedStyle()`) can animate its card on the UI thread. Pass `import Reanimated from 'react-native-reanimated'`; omit to keep Dialog's `animatedStyle` prop a no-op. */
|
|
328
328
|
reanimated?: ReanimatedModule;
|
|
329
|
+
/** Renders React Native's `StatusBar`, styled from the theme (here and in `Appbar.Header`). Default true. Pass false
|
|
330
|
+
* for an app that sets the status bar per screen instead (react-native-screens' `statusBarStyle`/`statusBarHidden`
|
|
331
|
+
* screen options, with `UIViewControllerBasedStatusBarAppearance` on): there React Native's StatusBar logs an error,
|
|
332
|
+
* and an app linked against the iOS 27 SDK can't set the status bar app-wide at all, which is the only way it works on iOS. */
|
|
333
|
+
statusBar?: boolean;
|
|
329
334
|
statusBarProps?: StatusBarProps;
|
|
330
335
|
style?: StyleProp<ViewStyle>;
|
|
331
336
|
};
|
|
332
|
-
declare function Provider({ children, defaults, expoBlur, fontFamily, initialValue, maskedView, navigationBar, onChange, onNavBarChange, onReady, reanimated, statusBarProps, style }: ProviderProps): react.JSX.Element;
|
|
337
|
+
declare function Provider({ children, defaults, expoBlur, fontFamily, initialValue, maskedView, navigationBar, onChange, onNavBarChange, onReady, reanimated, statusBar, statusBarProps, style }: ProviderProps): react.JSX.Element;
|
|
333
338
|
|
|
334
339
|
declare const useAutoPaperTheme: () => AutoPaperTheme;
|
|
335
340
|
|
package/dist/index.d.ts
CHANGED
|
@@ -257,19 +257,19 @@ declare const themeActions: {
|
|
|
257
257
|
type: string;
|
|
258
258
|
}) => action is _rific_core.SettingsAction<string | TriadicPalette>;
|
|
259
259
|
};
|
|
260
|
-
|
|
261
|
-
(payload:
|
|
260
|
+
setBlur: {
|
|
261
|
+
(payload: boolean): _rific_core.SettingsAction<boolean>;
|
|
262
262
|
type: string;
|
|
263
263
|
match: (action: {
|
|
264
264
|
type: string;
|
|
265
|
-
}) => action is _rific_core.SettingsAction<
|
|
265
|
+
}) => action is _rific_core.SettingsAction<boolean>;
|
|
266
266
|
};
|
|
267
|
-
|
|
268
|
-
(payload:
|
|
267
|
+
setAppearance: {
|
|
268
|
+
(payload: ThemeAppearance): _rific_core.SettingsAction<ThemeAppearance>;
|
|
269
269
|
type: string;
|
|
270
270
|
match: (action: {
|
|
271
271
|
type: string;
|
|
272
|
-
}) => action is _rific_core.SettingsAction<
|
|
272
|
+
}) => action is _rific_core.SettingsAction<ThemeAppearance>;
|
|
273
273
|
};
|
|
274
274
|
setHarmony: {
|
|
275
275
|
(payload: ColorHarmony): _rific_core.SettingsAction<ColorHarmony>;
|
|
@@ -326,10 +326,15 @@ type ProviderProps = {
|
|
|
326
326
|
onReady?: () => void;
|
|
327
327
|
/** Injects react-native-reanimated so `<Dialog animatedStyle={...}>` (e.g. from `useAnimatedStyle()`) can animate its card on the UI thread. Pass `import Reanimated from 'react-native-reanimated'`; omit to keep Dialog's `animatedStyle` prop a no-op. */
|
|
328
328
|
reanimated?: ReanimatedModule;
|
|
329
|
+
/** Renders React Native's `StatusBar`, styled from the theme (here and in `Appbar.Header`). Default true. Pass false
|
|
330
|
+
* for an app that sets the status bar per screen instead (react-native-screens' `statusBarStyle`/`statusBarHidden`
|
|
331
|
+
* screen options, with `UIViewControllerBasedStatusBarAppearance` on): there React Native's StatusBar logs an error,
|
|
332
|
+
* and an app linked against the iOS 27 SDK can't set the status bar app-wide at all, which is the only way it works on iOS. */
|
|
333
|
+
statusBar?: boolean;
|
|
329
334
|
statusBarProps?: StatusBarProps;
|
|
330
335
|
style?: StyleProp<ViewStyle>;
|
|
331
336
|
};
|
|
332
|
-
declare function Provider({ children, defaults, expoBlur, fontFamily, initialValue, maskedView, navigationBar, onChange, onNavBarChange, onReady, reanimated, statusBarProps, style }: ProviderProps): react.JSX.Element;
|
|
337
|
+
declare function Provider({ children, defaults, expoBlur, fontFamily, initialValue, maskedView, navigationBar, onChange, onNavBarChange, onReady, reanimated, statusBar, statusBarProps, style }: ProviderProps): react.JSX.Element;
|
|
333
338
|
|
|
334
339
|
declare const useAutoPaperTheme: () => AutoPaperTheme;
|
|
335
340
|
|
package/dist/index.js
CHANGED
|
@@ -108,8 +108,8 @@ function useBlur(override) {
|
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
// src/components/Appbar.tsx
|
|
111
|
-
var
|
|
112
|
-
var
|
|
111
|
+
var import_react_native3 = require("react-native");
|
|
112
|
+
var import_react_native_paper3 = require("react-native-paper");
|
|
113
113
|
|
|
114
114
|
// src/PaperDefaultsContext.tsx
|
|
115
115
|
var import_react2 = require("react");
|
|
@@ -118,69 +118,15 @@ function usePaperDefaults() {
|
|
|
118
118
|
return (0, import_react2.useContext)(PaperDefaultsContext);
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
-
// src/
|
|
122
|
-
var
|
|
123
|
-
|
|
124
|
-
const defaults = usePaperDefaults();
|
|
125
|
-
const theme = (0, import_react_native_paper.useTheme)();
|
|
126
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
127
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native.StatusBar, { backgroundColor: theme.colors.surface, barStyle: theme.dark ? "light-content" : "dark-content" }),
|
|
128
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native_paper.Appbar.Header, { ...defaults.AppbarHeader, ...props, style })
|
|
129
|
-
] });
|
|
130
|
-
}
|
|
131
|
-
var Appbar = {
|
|
132
|
-
Header,
|
|
133
|
-
Content: import_react_native_paper.Appbar.Content,
|
|
134
|
-
Action: import_react_native_paper.Appbar.Action,
|
|
135
|
-
BackAction: import_react_native_paper.Appbar.BackAction
|
|
136
|
-
};
|
|
137
|
-
|
|
138
|
-
// src/components/AppearancePicker.tsx
|
|
121
|
+
// src/ThemeProvider.tsx
|
|
122
|
+
var import_react4 = require("react");
|
|
123
|
+
var import_react_native2 = require("react-native");
|
|
139
124
|
var import_react_native_paper2 = require("react-native-paper");
|
|
140
|
-
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
141
|
-
var APPEARANCES = [
|
|
142
|
-
{ label: "System", value: "system" },
|
|
143
|
-
{ label: "Light", value: "light" },
|
|
144
|
-
{ label: "Dark", value: "dark" }
|
|
145
|
-
];
|
|
146
|
-
var DEFAULT_ICONS = {
|
|
147
|
-
system: "monitor",
|
|
148
|
-
light: "white-balance-sunny",
|
|
149
|
-
dark: "weather-night"
|
|
150
|
-
};
|
|
151
|
-
var AppearancePicker = ({ value, onChange, showLabels = true, icons }) => {
|
|
152
|
-
const resolvedIcons = { ...DEFAULT_ICONS, ...icons };
|
|
153
|
-
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_paper2.SegmentedButtons, { value, onValueChange: (v) => onChange(v), buttons: APPEARANCES.map((a) => ({ value: a.value, label: showLabels ? a.label : void 0, icon: resolvedIcons[a.value], accessibilityLabel: a.label })) });
|
|
154
|
-
};
|
|
155
|
-
|
|
156
|
-
// src/useThemeSettings.ts
|
|
157
|
-
var useThemeSettings = useThemeSettingsInternal;
|
|
158
|
-
|
|
159
|
-
// src/components/AutoAppearancePicker.tsx
|
|
160
|
-
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
161
|
-
var AutoAppearancePicker = (props) => {
|
|
162
|
-
const { settings, set } = useThemeSettings();
|
|
163
|
-
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(AppearancePicker, { ...props, value: settings.appearance, onChange: (appearance) => set({ appearance }) });
|
|
164
|
-
};
|
|
165
|
-
|
|
166
|
-
// src/utils/resolveSeedColor.ts
|
|
167
|
-
var resolveSeedColor = (color) => typeof color === "string" ? color : color.primary;
|
|
168
|
-
|
|
169
|
-
// src/components/PalettePicker.tsx
|
|
170
|
-
var import_react9 = require("react");
|
|
171
|
-
var import_react_native7 = require("react-native");
|
|
172
|
-
var import_react_native_paper9 = require("react-native-paper");
|
|
173
125
|
|
|
174
|
-
// src/
|
|
126
|
+
// src/useComputedTheme.ts
|
|
175
127
|
var import_react3 = require("react");
|
|
176
|
-
var
|
|
177
|
-
var
|
|
178
|
-
function useSwatchSize(fallback) {
|
|
179
|
-
const [width, setWidth] = (0, import_react3.useState)(0);
|
|
180
|
-
const onLayout = ({ nativeEvent }) => setWidth(nativeEvent.layout.width);
|
|
181
|
-
const size = width > 0 ? (width - SWATCH_GRID_GAP * (SWATCH_GRID_COLUMNS - 1)) / SWATCH_GRID_COLUMNS : fallback;
|
|
182
|
-
return { onLayout, size };
|
|
183
|
-
}
|
|
128
|
+
var import_react_native = require("react-native");
|
|
129
|
+
var import_react_native_paper = require("react-native-paper");
|
|
184
130
|
|
|
185
131
|
// src/utils/colorNames.ts
|
|
186
132
|
var colorNames = {
|
|
@@ -380,25 +326,6 @@ var getRgb = (value) => {
|
|
|
380
326
|
return null;
|
|
381
327
|
};
|
|
382
328
|
|
|
383
|
-
// src/utils/isDarkColor.ts
|
|
384
|
-
var isDarkColor = (value) => {
|
|
385
|
-
const rgb = getRgb(value);
|
|
386
|
-
if (!rgb) return false;
|
|
387
|
-
const [r, g, b] = [rgb.r / 255, rgb.g / 255, rgb.b / 255];
|
|
388
|
-
const [rs, gs, bs] = [r, g, b].map((c) => c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4));
|
|
389
|
-
const luminance = 0.2126 * rs + 0.7152 * gs + 0.0722 * bs;
|
|
390
|
-
return luminance < 0.5;
|
|
391
|
-
};
|
|
392
|
-
|
|
393
|
-
// src/utils/getSwatchContrast.ts
|
|
394
|
-
var RING_WIDTH = 1;
|
|
395
|
-
var SELECTED_RING_WIDTH = 3;
|
|
396
|
-
var getContrastColor = (color) => isDarkColor(color) ? "#ffffff" : "#000000";
|
|
397
|
-
var getSwatchRing = (color, selected, outlineColor) => ({
|
|
398
|
-
borderColor: selected ? getContrastColor(color) : outlineColor,
|
|
399
|
-
borderWidth: selected ? SELECTED_RING_WIDTH : RING_WIDTH
|
|
400
|
-
});
|
|
401
|
-
|
|
402
329
|
// src/utils/getHex.ts
|
|
403
330
|
var getHex = (value) => {
|
|
404
331
|
const rgb = getRgb(value);
|
|
@@ -414,6 +341,92 @@ var getHex = (value) => {
|
|
|
414
341
|
return hexString;
|
|
415
342
|
};
|
|
416
343
|
|
|
344
|
+
// src/utils/getBlendedColor.ts
|
|
345
|
+
var getBlendedColor = (foreground, background, alpha) => {
|
|
346
|
+
const fg = getRgb(foreground);
|
|
347
|
+
const bg = getRgb(background);
|
|
348
|
+
if (!fg || !bg) return background;
|
|
349
|
+
const safeAlpha = Math.min(1, Math.max(0, alpha));
|
|
350
|
+
const r = Math.round(fg.r * safeAlpha + bg.r * (1 - safeAlpha));
|
|
351
|
+
const g = Math.round(fg.g * safeAlpha + bg.g * (1 - safeAlpha));
|
|
352
|
+
const b = Math.round(fg.b * safeAlpha + bg.b * (1 - safeAlpha));
|
|
353
|
+
return getHex(`rgb(${r}, ${g}, ${b})`) ?? background;
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
// src/utils/getTonalColor.ts
|
|
357
|
+
var getTonalColor = (color, lightness) => {
|
|
358
|
+
const rgb = getRgb(color);
|
|
359
|
+
if (!rgb) return color;
|
|
360
|
+
const r = rgb.r / 255;
|
|
361
|
+
const g = rgb.g / 255;
|
|
362
|
+
const b = rgb.b / 255;
|
|
363
|
+
const max = Math.max(r, g, b);
|
|
364
|
+
const min = Math.min(r, g, b);
|
|
365
|
+
const delta = max - min;
|
|
366
|
+
let h = 0;
|
|
367
|
+
if (delta !== 0) {
|
|
368
|
+
if (max === r) h = (g - b) / delta % 6;
|
|
369
|
+
else if (max === g) h = (b - r) / delta + 2;
|
|
370
|
+
else h = (r - g) / delta + 4;
|
|
371
|
+
h = (h * 60 + 360) % 360;
|
|
372
|
+
}
|
|
373
|
+
const l = (max + min) / 2;
|
|
374
|
+
const s = delta === 0 ? 0 : delta / (1 - Math.abs(2 * l - 1));
|
|
375
|
+
const targetL = Math.min(1, Math.max(0, lightness));
|
|
376
|
+
const c = (1 - Math.abs(2 * targetL - 1)) * s;
|
|
377
|
+
const x = c * (1 - Math.abs(h / 60 % 2 - 1));
|
|
378
|
+
const m = targetL - c / 2;
|
|
379
|
+
let r2 = 0, g2 = 0, b2 = 0;
|
|
380
|
+
if (h < 60) {
|
|
381
|
+
r2 = c;
|
|
382
|
+
g2 = x;
|
|
383
|
+
b2 = 0;
|
|
384
|
+
} else if (h < 120) {
|
|
385
|
+
r2 = x;
|
|
386
|
+
g2 = c;
|
|
387
|
+
b2 = 0;
|
|
388
|
+
} else if (h < 180) {
|
|
389
|
+
r2 = 0;
|
|
390
|
+
g2 = c;
|
|
391
|
+
b2 = x;
|
|
392
|
+
} else if (h < 240) {
|
|
393
|
+
r2 = 0;
|
|
394
|
+
g2 = x;
|
|
395
|
+
b2 = c;
|
|
396
|
+
} else if (h < 300) {
|
|
397
|
+
r2 = x;
|
|
398
|
+
g2 = 0;
|
|
399
|
+
b2 = c;
|
|
400
|
+
} else {
|
|
401
|
+
r2 = c;
|
|
402
|
+
g2 = 0;
|
|
403
|
+
b2 = x;
|
|
404
|
+
}
|
|
405
|
+
const toInt = (n) => Math.round((n + m) * 255);
|
|
406
|
+
return getHex(`rgb(${toInt(r2)}, ${toInt(g2)}, ${toInt(b2)})`) ?? color;
|
|
407
|
+
};
|
|
408
|
+
|
|
409
|
+
// src/utils/isDarkColor.ts
|
|
410
|
+
var isDarkColor = (value) => {
|
|
411
|
+
const rgb = getRgb(value);
|
|
412
|
+
if (!rgb) return false;
|
|
413
|
+
const [r, g, b] = [rgb.r / 255, rgb.g / 255, rgb.b / 255];
|
|
414
|
+
const [rs, gs, bs] = [r, g, b].map((c) => c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4));
|
|
415
|
+
const luminance = 0.2126 * rs + 0.7152 * gs + 0.0722 * bs;
|
|
416
|
+
return luminance < 0.5;
|
|
417
|
+
};
|
|
418
|
+
|
|
419
|
+
// src/utils/getColorRoles.ts
|
|
420
|
+
var getColorRoles = (base, surface, containerAlpha = 0.15) => {
|
|
421
|
+
const container = getBlendedColor(base, surface, containerAlpha);
|
|
422
|
+
return {
|
|
423
|
+
color: base,
|
|
424
|
+
onColor: getTonalColor(base, isDarkColor(base) ? 0.95 : 0.08),
|
|
425
|
+
container,
|
|
426
|
+
onContainer: getTonalColor(base, isDarkColor(container) ? 0.92 : 0.12)
|
|
427
|
+
};
|
|
428
|
+
};
|
|
429
|
+
|
|
417
430
|
// src/utils/getTriadicPalette.ts
|
|
418
431
|
var HARMONY_OFFSETS = {
|
|
419
432
|
analogous: [1 / 12, 2 / 12],
|
|
@@ -495,103 +508,6 @@ function hslToHex(h, s, l) {
|
|
|
495
508
|
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
|
|
496
509
|
}
|
|
497
510
|
|
|
498
|
-
// src/components/ColorPicker.tsx
|
|
499
|
-
var import_react8 = require("react");
|
|
500
|
-
var import_react_native6 = require("react-native");
|
|
501
|
-
var import_react_native_paper7 = require("react-native-paper");
|
|
502
|
-
|
|
503
|
-
// src/components/Dialog.tsx
|
|
504
|
-
var import_react7 = __toESM(require("react"));
|
|
505
|
-
var import_react_native5 = require("react-native");
|
|
506
|
-
var import_react_native_paper6 = require("react-native-paper");
|
|
507
|
-
var import_react_native_safe_area_context = require("react-native-safe-area-context");
|
|
508
|
-
|
|
509
|
-
// src/ThemeProvider.tsx
|
|
510
|
-
var import_react5 = require("react");
|
|
511
|
-
var import_react_native3 = require("react-native");
|
|
512
|
-
var import_react_native_paper4 = require("react-native-paper");
|
|
513
|
-
|
|
514
|
-
// src/useComputedTheme.ts
|
|
515
|
-
var import_react4 = require("react");
|
|
516
|
-
var import_react_native2 = require("react-native");
|
|
517
|
-
var import_react_native_paper3 = require("react-native-paper");
|
|
518
|
-
|
|
519
|
-
// src/utils/getBlendedColor.ts
|
|
520
|
-
var getBlendedColor = (foreground, background, alpha) => {
|
|
521
|
-
const fg = getRgb(foreground);
|
|
522
|
-
const bg = getRgb(background);
|
|
523
|
-
if (!fg || !bg) return background;
|
|
524
|
-
const safeAlpha = Math.min(1, Math.max(0, alpha));
|
|
525
|
-
const r = Math.round(fg.r * safeAlpha + bg.r * (1 - safeAlpha));
|
|
526
|
-
const g = Math.round(fg.g * safeAlpha + bg.g * (1 - safeAlpha));
|
|
527
|
-
const b = Math.round(fg.b * safeAlpha + bg.b * (1 - safeAlpha));
|
|
528
|
-
return getHex(`rgb(${r}, ${g}, ${b})`) ?? background;
|
|
529
|
-
};
|
|
530
|
-
|
|
531
|
-
// src/utils/getTonalColor.ts
|
|
532
|
-
var getTonalColor = (color, lightness) => {
|
|
533
|
-
const rgb = getRgb(color);
|
|
534
|
-
if (!rgb) return color;
|
|
535
|
-
const r = rgb.r / 255;
|
|
536
|
-
const g = rgb.g / 255;
|
|
537
|
-
const b = rgb.b / 255;
|
|
538
|
-
const max = Math.max(r, g, b);
|
|
539
|
-
const min = Math.min(r, g, b);
|
|
540
|
-
const delta = max - min;
|
|
541
|
-
let h = 0;
|
|
542
|
-
if (delta !== 0) {
|
|
543
|
-
if (max === r) h = (g - b) / delta % 6;
|
|
544
|
-
else if (max === g) h = (b - r) / delta + 2;
|
|
545
|
-
else h = (r - g) / delta + 4;
|
|
546
|
-
h = (h * 60 + 360) % 360;
|
|
547
|
-
}
|
|
548
|
-
const l = (max + min) / 2;
|
|
549
|
-
const s = delta === 0 ? 0 : delta / (1 - Math.abs(2 * l - 1));
|
|
550
|
-
const targetL = Math.min(1, Math.max(0, lightness));
|
|
551
|
-
const c = (1 - Math.abs(2 * targetL - 1)) * s;
|
|
552
|
-
const x = c * (1 - Math.abs(h / 60 % 2 - 1));
|
|
553
|
-
const m = targetL - c / 2;
|
|
554
|
-
let r2 = 0, g2 = 0, b2 = 0;
|
|
555
|
-
if (h < 60) {
|
|
556
|
-
r2 = c;
|
|
557
|
-
g2 = x;
|
|
558
|
-
b2 = 0;
|
|
559
|
-
} else if (h < 120) {
|
|
560
|
-
r2 = x;
|
|
561
|
-
g2 = c;
|
|
562
|
-
b2 = 0;
|
|
563
|
-
} else if (h < 180) {
|
|
564
|
-
r2 = 0;
|
|
565
|
-
g2 = c;
|
|
566
|
-
b2 = x;
|
|
567
|
-
} else if (h < 240) {
|
|
568
|
-
r2 = 0;
|
|
569
|
-
g2 = x;
|
|
570
|
-
b2 = c;
|
|
571
|
-
} else if (h < 300) {
|
|
572
|
-
r2 = x;
|
|
573
|
-
g2 = 0;
|
|
574
|
-
b2 = c;
|
|
575
|
-
} else {
|
|
576
|
-
r2 = c;
|
|
577
|
-
g2 = 0;
|
|
578
|
-
b2 = x;
|
|
579
|
-
}
|
|
580
|
-
const toInt = (n) => Math.round((n + m) * 255);
|
|
581
|
-
return getHex(`rgb(${toInt(r2)}, ${toInt(g2)}, ${toInt(b2)})`) ?? color;
|
|
582
|
-
};
|
|
583
|
-
|
|
584
|
-
// src/utils/getColorRoles.ts
|
|
585
|
-
var getColorRoles = (base, surface, containerAlpha = 0.15) => {
|
|
586
|
-
const container = getBlendedColor(base, surface, containerAlpha);
|
|
587
|
-
return {
|
|
588
|
-
color: base,
|
|
589
|
-
onColor: getTonalColor(base, isDarkColor(base) ? 0.95 : 0.08),
|
|
590
|
-
container,
|
|
591
|
-
onContainer: getTonalColor(base, isDarkColor(container) ? 0.92 : 0.12)
|
|
592
|
-
};
|
|
593
|
-
};
|
|
594
|
-
|
|
595
511
|
// src/useComputedTheme.ts
|
|
596
512
|
function normalizeExplicitColor(value) {
|
|
597
513
|
if (!getRgb(value)) throw new Error(`Invalid color format: ${value}`);
|
|
@@ -603,15 +519,15 @@ var SEMANTIC_BASE_COLORS = {
|
|
|
603
519
|
danger: "#B00020"
|
|
604
520
|
};
|
|
605
521
|
function useComputedTheme(appearance, color, harmony = "split-complementary") {
|
|
606
|
-
const [theme, setTheme] = (0,
|
|
522
|
+
const [theme, setTheme] = (0, import_react3.useState)(null);
|
|
607
523
|
const primaryInput = typeof color === "string" ? color : color.primary;
|
|
608
524
|
const secondaryInput = typeof color === "string" ? null : color.secondary;
|
|
609
525
|
const tertiaryInput = typeof color === "string" ? null : color.tertiary;
|
|
610
|
-
const computeTheme = (0,
|
|
526
|
+
const computeTheme = (0, import_react3.useCallback)(() => {
|
|
611
527
|
let resolved = appearance;
|
|
612
|
-
if (resolved === "system") resolved =
|
|
528
|
+
if (resolved === "system") resolved = import_react_native.Appearance.getColorScheme();
|
|
613
529
|
if (resolved === "no-preference" || !resolved) resolved = "light";
|
|
614
|
-
const base = resolved === "dark" ?
|
|
530
|
+
const base = resolved === "dark" ? import_react_native_paper.MD3DarkTheme : import_react_native_paper.MD3LightTheme;
|
|
615
531
|
const surface = base.colors.surface;
|
|
616
532
|
const successRoles = getColorRoles(SEMANTIC_BASE_COLORS.success, surface);
|
|
617
533
|
const warningRoles = getColorRoles(SEMANTIC_BASE_COLORS.warning, surface);
|
|
@@ -669,55 +585,142 @@ function useComputedTheme(appearance, color, harmony = "split-complementary") {
|
|
|
669
585
|
};
|
|
670
586
|
setTheme(_theme);
|
|
671
587
|
}, [appearance, primaryInput, secondaryInput, tertiaryInput, harmony]);
|
|
672
|
-
(0,
|
|
588
|
+
(0, import_react3.useEffect)(() => {
|
|
673
589
|
computeTheme();
|
|
674
590
|
}, [computeTheme]);
|
|
675
|
-
(0,
|
|
676
|
-
const subscription =
|
|
591
|
+
(0, import_react3.useEffect)(() => {
|
|
592
|
+
const subscription = import_react_native.Appearance.addChangeListener(computeTheme);
|
|
677
593
|
return subscription.remove;
|
|
678
594
|
}, [computeTheme]);
|
|
679
595
|
return theme;
|
|
680
596
|
}
|
|
681
597
|
|
|
682
598
|
// src/ThemeProvider.tsx
|
|
683
|
-
var
|
|
684
|
-
var NavBarContext = (0,
|
|
685
|
-
var useNavBarContext = () => (0,
|
|
686
|
-
var BlurModuleContext = (0,
|
|
687
|
-
var useBlurModule = () => (0,
|
|
688
|
-
var ReanimatedModuleContext = (0,
|
|
689
|
-
var useReanimatedModule = () => (0,
|
|
690
|
-
var MaskedViewModuleContext = (0,
|
|
691
|
-
var useMaskedViewModule = () => (0,
|
|
692
|
-
|
|
693
|
-
|
|
599
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
600
|
+
var NavBarContext = (0, import_react4.createContext)({});
|
|
601
|
+
var useNavBarContext = () => (0, import_react4.useContext)(NavBarContext);
|
|
602
|
+
var BlurModuleContext = (0, import_react4.createContext)(void 0);
|
|
603
|
+
var useBlurModule = () => (0, import_react4.useContext)(BlurModuleContext);
|
|
604
|
+
var ReanimatedModuleContext = (0, import_react4.createContext)(void 0);
|
|
605
|
+
var useReanimatedModule = () => (0, import_react4.useContext)(ReanimatedModuleContext);
|
|
606
|
+
var MaskedViewModuleContext = (0, import_react4.createContext)(void 0);
|
|
607
|
+
var useMaskedViewModule = () => (0, import_react4.useContext)(MaskedViewModuleContext);
|
|
608
|
+
var StatusBarEnabledContext = (0, import_react4.createContext)(true);
|
|
609
|
+
var useStatusBarEnabled = () => (0, import_react4.useContext)(StatusBarEnabledContext);
|
|
610
|
+
function Provider({ children, defaults, expoBlur, fontFamily, initialValue, maskedView, navigationBar, onChange, onNavBarChange, onReady, reanimated, statusBar, statusBarProps, style }) {
|
|
611
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ThemeSettingsProvider, { initialValue, onChange, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ThemeProviderBody, { defaults, expoBlur, fontFamily, maskedView, navigationBar, onNavBarChange, onReady, reanimated, statusBar, statusBarProps, style, children }) });
|
|
694
612
|
}
|
|
695
613
|
var ThemeSettingsProvider = themeSettingsProvider;
|
|
696
|
-
function ThemeProviderBody({ children, defaults, expoBlur, fontFamily, maskedView, navigationBar, onNavBarChange, onReady, reanimated, statusBarProps, style }) {
|
|
614
|
+
function ThemeProviderBody({ children, defaults, expoBlur, fontFamily, maskedView, navigationBar, onNavBarChange, onReady, reanimated, statusBar = true, statusBarProps, style }) {
|
|
697
615
|
const { settings } = useThemeSettingsInternal();
|
|
698
616
|
const computedTheme = useComputedTheme(settings.appearance, settings.color, settings.harmony);
|
|
699
|
-
const theme = (0,
|
|
617
|
+
const theme = (0, import_react4.useMemo)(() => {
|
|
700
618
|
if (!computedTheme) return null;
|
|
701
619
|
if (!fontFamily) return computedTheme;
|
|
702
|
-
return { ...computedTheme, fonts: (0,
|
|
620
|
+
return { ...computedTheme, fonts: (0, import_react_native_paper2.configureFonts)({ config: { fontFamily } }) };
|
|
703
621
|
}, [computedTheme, fontFamily]);
|
|
704
|
-
const called = (0,
|
|
705
|
-
(0,
|
|
622
|
+
const called = (0, import_react4.useRef)(false);
|
|
623
|
+
(0, import_react4.useEffect)(() => {
|
|
706
624
|
if (theme && !called.current) {
|
|
707
625
|
called.current = true;
|
|
708
626
|
onReady?.();
|
|
709
627
|
}
|
|
710
628
|
}, [theme, onReady]);
|
|
711
629
|
if (!theme) return null;
|
|
712
|
-
return /* @__PURE__ */ (0,
|
|
713
|
-
/* @__PURE__ */ (0,
|
|
714
|
-
/* @__PURE__ */ (0,
|
|
630
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native_paper2.Provider, { theme, children: [
|
|
631
|
+
statusBar && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native2.StatusBar, { backgroundColor: theme.colors.background, barStyle: theme.dark ? "light-content" : "dark-content", ...statusBarProps }),
|
|
632
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatusBarEnabledContext.Provider, { value: statusBar, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PaperDefaultsContext.Provider, { value: defaults ?? {}, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(NavBarContext.Provider, { value: { navigationBar, onNavBarChange }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BlurModuleContext.Provider, { value: expoBlur, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ReanimatedModuleContext.Provider, { value: reanimated, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MaskedViewModuleContext.Provider, { value: maskedView, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native_paper2.Portal.Host, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native2.View, { style: [styles.flex, { backgroundColor: theme.colors.background }, style], children }) }) }) }) }) }) }) })
|
|
715
633
|
] });
|
|
716
634
|
}
|
|
717
|
-
var styles =
|
|
635
|
+
var styles = import_react_native2.StyleSheet.create({
|
|
718
636
|
flex: { flex: 1 }
|
|
719
637
|
});
|
|
720
638
|
|
|
639
|
+
// src/components/Appbar.tsx
|
|
640
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
641
|
+
function Header({ style, ...props }) {
|
|
642
|
+
const defaults = usePaperDefaults();
|
|
643
|
+
const theme = (0, import_react_native_paper3.useTheme)();
|
|
644
|
+
const statusBar = useStatusBarEnabled();
|
|
645
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
|
|
646
|
+
statusBar && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.StatusBar, { backgroundColor: theme.colors.surface, barStyle: theme.dark ? "light-content" : "dark-content" }),
|
|
647
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native_paper3.Appbar.Header, { ...defaults.AppbarHeader, ...props, style })
|
|
648
|
+
] });
|
|
649
|
+
}
|
|
650
|
+
var Appbar = {
|
|
651
|
+
Header,
|
|
652
|
+
Content: import_react_native_paper3.Appbar.Content,
|
|
653
|
+
Action: import_react_native_paper3.Appbar.Action,
|
|
654
|
+
BackAction: import_react_native_paper3.Appbar.BackAction
|
|
655
|
+
};
|
|
656
|
+
|
|
657
|
+
// src/components/AppearancePicker.tsx
|
|
658
|
+
var import_react_native_paper4 = require("react-native-paper");
|
|
659
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
660
|
+
var APPEARANCES = [
|
|
661
|
+
{ label: "System", value: "system" },
|
|
662
|
+
{ label: "Light", value: "light" },
|
|
663
|
+
{ label: "Dark", value: "dark" }
|
|
664
|
+
];
|
|
665
|
+
var DEFAULT_ICONS = {
|
|
666
|
+
system: "monitor",
|
|
667
|
+
light: "white-balance-sunny",
|
|
668
|
+
dark: "weather-night"
|
|
669
|
+
};
|
|
670
|
+
var AppearancePicker = ({ value, onChange, showLabels = true, icons }) => {
|
|
671
|
+
const resolvedIcons = { ...DEFAULT_ICONS, ...icons };
|
|
672
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native_paper4.SegmentedButtons, { value, onValueChange: (v) => onChange(v), buttons: APPEARANCES.map((a) => ({ value: a.value, label: showLabels ? a.label : void 0, icon: resolvedIcons[a.value], accessibilityLabel: a.label })) });
|
|
673
|
+
};
|
|
674
|
+
|
|
675
|
+
// src/useThemeSettings.ts
|
|
676
|
+
var useThemeSettings = useThemeSettingsInternal;
|
|
677
|
+
|
|
678
|
+
// src/components/AutoAppearancePicker.tsx
|
|
679
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
680
|
+
var AutoAppearancePicker = (props) => {
|
|
681
|
+
const { settings, set } = useThemeSettings();
|
|
682
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(AppearancePicker, { ...props, value: settings.appearance, onChange: (appearance) => set({ appearance }) });
|
|
683
|
+
};
|
|
684
|
+
|
|
685
|
+
// src/utils/resolveSeedColor.ts
|
|
686
|
+
var resolveSeedColor = (color) => typeof color === "string" ? color : color.primary;
|
|
687
|
+
|
|
688
|
+
// src/components/PalettePicker.tsx
|
|
689
|
+
var import_react9 = require("react");
|
|
690
|
+
var import_react_native7 = require("react-native");
|
|
691
|
+
var import_react_native_paper9 = require("react-native-paper");
|
|
692
|
+
|
|
693
|
+
// src/useSwatchGrid.ts
|
|
694
|
+
var import_react5 = require("react");
|
|
695
|
+
var SWATCH_GRID_COLUMNS = 5;
|
|
696
|
+
var SWATCH_GRID_GAP = 12;
|
|
697
|
+
function useSwatchSize(fallback) {
|
|
698
|
+
const [width, setWidth] = (0, import_react5.useState)(0);
|
|
699
|
+
const onLayout = ({ nativeEvent }) => setWidth(nativeEvent.layout.width);
|
|
700
|
+
const size = width > 0 ? (width - SWATCH_GRID_GAP * (SWATCH_GRID_COLUMNS - 1)) / SWATCH_GRID_COLUMNS : fallback;
|
|
701
|
+
return { onLayout, size };
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
// src/utils/getSwatchContrast.ts
|
|
705
|
+
var RING_WIDTH = 1;
|
|
706
|
+
var SELECTED_RING_WIDTH = 3;
|
|
707
|
+
var getContrastColor = (color) => isDarkColor(color) ? "#ffffff" : "#000000";
|
|
708
|
+
var getSwatchRing = (color, selected, outlineColor) => ({
|
|
709
|
+
borderColor: selected ? getContrastColor(color) : outlineColor,
|
|
710
|
+
borderWidth: selected ? SELECTED_RING_WIDTH : RING_WIDTH
|
|
711
|
+
});
|
|
712
|
+
|
|
713
|
+
// src/components/ColorPicker.tsx
|
|
714
|
+
var import_react8 = require("react");
|
|
715
|
+
var import_react_native6 = require("react-native");
|
|
716
|
+
var import_react_native_paper7 = require("react-native-paper");
|
|
717
|
+
|
|
718
|
+
// src/components/Dialog.tsx
|
|
719
|
+
var import_react7 = __toESM(require("react"));
|
|
720
|
+
var import_react_native5 = require("react-native");
|
|
721
|
+
var import_react_native_paper6 = require("react-native-paper");
|
|
722
|
+
var import_react_native_safe_area_context = require("react-native-safe-area-context");
|
|
723
|
+
|
|
721
724
|
// src/components/BlurView.tsx
|
|
722
725
|
var import_react6 = require("react");
|
|
723
726
|
var import_react_native4 = require("react-native");
|
package/dist/index.mjs
CHANGED
|
@@ -22,7 +22,7 @@ function useBlur(override) {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
// src/components/Appbar.tsx
|
|
25
|
-
import { StatusBar } from "react-native";
|
|
25
|
+
import { StatusBar as StatusBar2 } from "react-native";
|
|
26
26
|
import { Appbar as PaperAppbar, useTheme } from "react-native-paper";
|
|
27
27
|
|
|
28
28
|
// src/PaperDefaultsContext.tsx
|
|
@@ -32,69 +32,15 @@ function usePaperDefaults() {
|
|
|
32
32
|
return useContext2(PaperDefaultsContext);
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
// src/
|
|
36
|
-
import {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const theme = useTheme();
|
|
40
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
41
|
-
/* @__PURE__ */ jsx(StatusBar, { backgroundColor: theme.colors.surface, barStyle: theme.dark ? "light-content" : "dark-content" }),
|
|
42
|
-
/* @__PURE__ */ jsx(PaperAppbar.Header, { ...defaults.AppbarHeader, ...props, style })
|
|
43
|
-
] });
|
|
44
|
-
}
|
|
45
|
-
var Appbar = {
|
|
46
|
-
Header,
|
|
47
|
-
Content: PaperAppbar.Content,
|
|
48
|
-
Action: PaperAppbar.Action,
|
|
49
|
-
BackAction: PaperAppbar.BackAction
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
// src/components/AppearancePicker.tsx
|
|
53
|
-
import { SegmentedButtons } from "react-native-paper";
|
|
54
|
-
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
55
|
-
var APPEARANCES = [
|
|
56
|
-
{ label: "System", value: "system" },
|
|
57
|
-
{ label: "Light", value: "light" },
|
|
58
|
-
{ label: "Dark", value: "dark" }
|
|
59
|
-
];
|
|
60
|
-
var DEFAULT_ICONS = {
|
|
61
|
-
system: "monitor",
|
|
62
|
-
light: "white-balance-sunny",
|
|
63
|
-
dark: "weather-night"
|
|
64
|
-
};
|
|
65
|
-
var AppearancePicker = ({ value, onChange, showLabels = true, icons }) => {
|
|
66
|
-
const resolvedIcons = { ...DEFAULT_ICONS, ...icons };
|
|
67
|
-
return /* @__PURE__ */ jsx2(SegmentedButtons, { value, onValueChange: (v) => onChange(v), buttons: APPEARANCES.map((a) => ({ value: a.value, label: showLabels ? a.label : void 0, icon: resolvedIcons[a.value], accessibilityLabel: a.label })) });
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
// src/useThemeSettings.ts
|
|
71
|
-
var useThemeSettings = useThemeSettingsInternal;
|
|
72
|
-
|
|
73
|
-
// src/components/AutoAppearancePicker.tsx
|
|
74
|
-
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
75
|
-
var AutoAppearancePicker = (props) => {
|
|
76
|
-
const { settings, set } = useThemeSettings();
|
|
77
|
-
return /* @__PURE__ */ jsx3(AppearancePicker, { ...props, value: settings.appearance, onChange: (appearance) => set({ appearance }) });
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
// src/utils/resolveSeedColor.ts
|
|
81
|
-
var resolveSeedColor = (color) => typeof color === "string" ? color : color.primary;
|
|
82
|
-
|
|
83
|
-
// src/components/PalettePicker.tsx
|
|
84
|
-
import { useState as useState5 } from "react";
|
|
85
|
-
import { StyleSheet as StyleSheet5, TouchableOpacity as TouchableOpacity2, View as View5 } from "react-native";
|
|
86
|
-
import { Icon as Icon2, TouchableRipple as TouchableRipple2, useTheme as useTheme5 } from "react-native-paper";
|
|
35
|
+
// src/ThemeProvider.tsx
|
|
36
|
+
import { createContext as createContext2, useContext as useContext3, useEffect as useEffect2, useMemo, useRef } from "react";
|
|
37
|
+
import { StatusBar, StyleSheet, View } from "react-native";
|
|
38
|
+
import { configureFonts, Portal, Provider as PaperProvider } from "react-native-paper";
|
|
87
39
|
|
|
88
|
-
// src/
|
|
89
|
-
import { useState } from "react";
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
function useSwatchSize(fallback) {
|
|
93
|
-
const [width, setWidth] = useState(0);
|
|
94
|
-
const onLayout = ({ nativeEvent }) => setWidth(nativeEvent.layout.width);
|
|
95
|
-
const size = width > 0 ? (width - SWATCH_GRID_GAP * (SWATCH_GRID_COLUMNS - 1)) / SWATCH_GRID_COLUMNS : fallback;
|
|
96
|
-
return { onLayout, size };
|
|
97
|
-
}
|
|
40
|
+
// src/useComputedTheme.ts
|
|
41
|
+
import { useCallback, useEffect, useState } from "react";
|
|
42
|
+
import { Appearance } from "react-native";
|
|
43
|
+
import { MD3DarkTheme, MD3LightTheme } from "react-native-paper";
|
|
98
44
|
|
|
99
45
|
// src/utils/colorNames.ts
|
|
100
46
|
var colorNames = {
|
|
@@ -294,25 +240,6 @@ var getRgb = (value) => {
|
|
|
294
240
|
return null;
|
|
295
241
|
};
|
|
296
242
|
|
|
297
|
-
// src/utils/isDarkColor.ts
|
|
298
|
-
var isDarkColor = (value) => {
|
|
299
|
-
const rgb = getRgb(value);
|
|
300
|
-
if (!rgb) return false;
|
|
301
|
-
const [r, g, b] = [rgb.r / 255, rgb.g / 255, rgb.b / 255];
|
|
302
|
-
const [rs, gs, bs] = [r, g, b].map((c) => c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4));
|
|
303
|
-
const luminance = 0.2126 * rs + 0.7152 * gs + 0.0722 * bs;
|
|
304
|
-
return luminance < 0.5;
|
|
305
|
-
};
|
|
306
|
-
|
|
307
|
-
// src/utils/getSwatchContrast.ts
|
|
308
|
-
var RING_WIDTH = 1;
|
|
309
|
-
var SELECTED_RING_WIDTH = 3;
|
|
310
|
-
var getContrastColor = (color) => isDarkColor(color) ? "#ffffff" : "#000000";
|
|
311
|
-
var getSwatchRing = (color, selected, outlineColor) => ({
|
|
312
|
-
borderColor: selected ? getContrastColor(color) : outlineColor,
|
|
313
|
-
borderWidth: selected ? SELECTED_RING_WIDTH : RING_WIDTH
|
|
314
|
-
});
|
|
315
|
-
|
|
316
243
|
// src/utils/getHex.ts
|
|
317
244
|
var getHex = (value) => {
|
|
318
245
|
const rgb = getRgb(value);
|
|
@@ -328,6 +255,92 @@ var getHex = (value) => {
|
|
|
328
255
|
return hexString;
|
|
329
256
|
};
|
|
330
257
|
|
|
258
|
+
// src/utils/getBlendedColor.ts
|
|
259
|
+
var getBlendedColor = (foreground, background, alpha) => {
|
|
260
|
+
const fg = getRgb(foreground);
|
|
261
|
+
const bg = getRgb(background);
|
|
262
|
+
if (!fg || !bg) return background;
|
|
263
|
+
const safeAlpha = Math.min(1, Math.max(0, alpha));
|
|
264
|
+
const r = Math.round(fg.r * safeAlpha + bg.r * (1 - safeAlpha));
|
|
265
|
+
const g = Math.round(fg.g * safeAlpha + bg.g * (1 - safeAlpha));
|
|
266
|
+
const b = Math.round(fg.b * safeAlpha + bg.b * (1 - safeAlpha));
|
|
267
|
+
return getHex(`rgb(${r}, ${g}, ${b})`) ?? background;
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
// src/utils/getTonalColor.ts
|
|
271
|
+
var getTonalColor = (color, lightness) => {
|
|
272
|
+
const rgb = getRgb(color);
|
|
273
|
+
if (!rgb) return color;
|
|
274
|
+
const r = rgb.r / 255;
|
|
275
|
+
const g = rgb.g / 255;
|
|
276
|
+
const b = rgb.b / 255;
|
|
277
|
+
const max = Math.max(r, g, b);
|
|
278
|
+
const min = Math.min(r, g, b);
|
|
279
|
+
const delta = max - min;
|
|
280
|
+
let h = 0;
|
|
281
|
+
if (delta !== 0) {
|
|
282
|
+
if (max === r) h = (g - b) / delta % 6;
|
|
283
|
+
else if (max === g) h = (b - r) / delta + 2;
|
|
284
|
+
else h = (r - g) / delta + 4;
|
|
285
|
+
h = (h * 60 + 360) % 360;
|
|
286
|
+
}
|
|
287
|
+
const l = (max + min) / 2;
|
|
288
|
+
const s = delta === 0 ? 0 : delta / (1 - Math.abs(2 * l - 1));
|
|
289
|
+
const targetL = Math.min(1, Math.max(0, lightness));
|
|
290
|
+
const c = (1 - Math.abs(2 * targetL - 1)) * s;
|
|
291
|
+
const x = c * (1 - Math.abs(h / 60 % 2 - 1));
|
|
292
|
+
const m = targetL - c / 2;
|
|
293
|
+
let r2 = 0, g2 = 0, b2 = 0;
|
|
294
|
+
if (h < 60) {
|
|
295
|
+
r2 = c;
|
|
296
|
+
g2 = x;
|
|
297
|
+
b2 = 0;
|
|
298
|
+
} else if (h < 120) {
|
|
299
|
+
r2 = x;
|
|
300
|
+
g2 = c;
|
|
301
|
+
b2 = 0;
|
|
302
|
+
} else if (h < 180) {
|
|
303
|
+
r2 = 0;
|
|
304
|
+
g2 = c;
|
|
305
|
+
b2 = x;
|
|
306
|
+
} else if (h < 240) {
|
|
307
|
+
r2 = 0;
|
|
308
|
+
g2 = x;
|
|
309
|
+
b2 = c;
|
|
310
|
+
} else if (h < 300) {
|
|
311
|
+
r2 = x;
|
|
312
|
+
g2 = 0;
|
|
313
|
+
b2 = c;
|
|
314
|
+
} else {
|
|
315
|
+
r2 = c;
|
|
316
|
+
g2 = 0;
|
|
317
|
+
b2 = x;
|
|
318
|
+
}
|
|
319
|
+
const toInt = (n) => Math.round((n + m) * 255);
|
|
320
|
+
return getHex(`rgb(${toInt(r2)}, ${toInt(g2)}, ${toInt(b2)})`) ?? color;
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
// src/utils/isDarkColor.ts
|
|
324
|
+
var isDarkColor = (value) => {
|
|
325
|
+
const rgb = getRgb(value);
|
|
326
|
+
if (!rgb) return false;
|
|
327
|
+
const [r, g, b] = [rgb.r / 255, rgb.g / 255, rgb.b / 255];
|
|
328
|
+
const [rs, gs, bs] = [r, g, b].map((c) => c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4));
|
|
329
|
+
const luminance = 0.2126 * rs + 0.7152 * gs + 0.0722 * bs;
|
|
330
|
+
return luminance < 0.5;
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
// src/utils/getColorRoles.ts
|
|
334
|
+
var getColorRoles = (base, surface, containerAlpha = 0.15) => {
|
|
335
|
+
const container = getBlendedColor(base, surface, containerAlpha);
|
|
336
|
+
return {
|
|
337
|
+
color: base,
|
|
338
|
+
onColor: getTonalColor(base, isDarkColor(base) ? 0.95 : 0.08),
|
|
339
|
+
container,
|
|
340
|
+
onContainer: getTonalColor(base, isDarkColor(container) ? 0.92 : 0.12)
|
|
341
|
+
};
|
|
342
|
+
};
|
|
343
|
+
|
|
331
344
|
// src/utils/getTriadicPalette.ts
|
|
332
345
|
var HARMONY_OFFSETS = {
|
|
333
346
|
analogous: [1 / 12, 2 / 12],
|
|
@@ -409,103 +422,6 @@ function hslToHex(h, s, l) {
|
|
|
409
422
|
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
|
|
410
423
|
}
|
|
411
424
|
|
|
412
|
-
// src/components/ColorPicker.tsx
|
|
413
|
-
import { useState as useState4 } from "react";
|
|
414
|
-
import { StyleSheet as StyleSheet4, TouchableOpacity, View as View4 } from "react-native";
|
|
415
|
-
import { Icon, TouchableRipple, useTheme as useTheme4 } from "react-native-paper";
|
|
416
|
-
|
|
417
|
-
// src/components/Dialog.tsx
|
|
418
|
-
import React, { useEffect as useEffect3, useState as useState3 } from "react";
|
|
419
|
-
import { Animated, BackHandler, Easing, Platform as Platform2, Pressable, StyleSheet as StyleSheet3, View as View3 } from "react-native";
|
|
420
|
-
import { Dialog as PaperDialog, Portal as Portal2, useTheme as useTheme3 } from "react-native-paper";
|
|
421
|
-
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
422
|
-
|
|
423
|
-
// src/ThemeProvider.tsx
|
|
424
|
-
import { createContext as createContext2, useContext as useContext3, useEffect as useEffect2, useMemo, useRef } from "react";
|
|
425
|
-
import { StatusBar as StatusBar2, StyleSheet, View } from "react-native";
|
|
426
|
-
import { configureFonts, Portal, Provider as PaperProvider } from "react-native-paper";
|
|
427
|
-
|
|
428
|
-
// src/useComputedTheme.ts
|
|
429
|
-
import { useCallback, useEffect, useState as useState2 } from "react";
|
|
430
|
-
import { Appearance } from "react-native";
|
|
431
|
-
import { MD3DarkTheme, MD3LightTheme } from "react-native-paper";
|
|
432
|
-
|
|
433
|
-
// src/utils/getBlendedColor.ts
|
|
434
|
-
var getBlendedColor = (foreground, background, alpha) => {
|
|
435
|
-
const fg = getRgb(foreground);
|
|
436
|
-
const bg = getRgb(background);
|
|
437
|
-
if (!fg || !bg) return background;
|
|
438
|
-
const safeAlpha = Math.min(1, Math.max(0, alpha));
|
|
439
|
-
const r = Math.round(fg.r * safeAlpha + bg.r * (1 - safeAlpha));
|
|
440
|
-
const g = Math.round(fg.g * safeAlpha + bg.g * (1 - safeAlpha));
|
|
441
|
-
const b = Math.round(fg.b * safeAlpha + bg.b * (1 - safeAlpha));
|
|
442
|
-
return getHex(`rgb(${r}, ${g}, ${b})`) ?? background;
|
|
443
|
-
};
|
|
444
|
-
|
|
445
|
-
// src/utils/getTonalColor.ts
|
|
446
|
-
var getTonalColor = (color, lightness) => {
|
|
447
|
-
const rgb = getRgb(color);
|
|
448
|
-
if (!rgb) return color;
|
|
449
|
-
const r = rgb.r / 255;
|
|
450
|
-
const g = rgb.g / 255;
|
|
451
|
-
const b = rgb.b / 255;
|
|
452
|
-
const max = Math.max(r, g, b);
|
|
453
|
-
const min = Math.min(r, g, b);
|
|
454
|
-
const delta = max - min;
|
|
455
|
-
let h = 0;
|
|
456
|
-
if (delta !== 0) {
|
|
457
|
-
if (max === r) h = (g - b) / delta % 6;
|
|
458
|
-
else if (max === g) h = (b - r) / delta + 2;
|
|
459
|
-
else h = (r - g) / delta + 4;
|
|
460
|
-
h = (h * 60 + 360) % 360;
|
|
461
|
-
}
|
|
462
|
-
const l = (max + min) / 2;
|
|
463
|
-
const s = delta === 0 ? 0 : delta / (1 - Math.abs(2 * l - 1));
|
|
464
|
-
const targetL = Math.min(1, Math.max(0, lightness));
|
|
465
|
-
const c = (1 - Math.abs(2 * targetL - 1)) * s;
|
|
466
|
-
const x = c * (1 - Math.abs(h / 60 % 2 - 1));
|
|
467
|
-
const m = targetL - c / 2;
|
|
468
|
-
let r2 = 0, g2 = 0, b2 = 0;
|
|
469
|
-
if (h < 60) {
|
|
470
|
-
r2 = c;
|
|
471
|
-
g2 = x;
|
|
472
|
-
b2 = 0;
|
|
473
|
-
} else if (h < 120) {
|
|
474
|
-
r2 = x;
|
|
475
|
-
g2 = c;
|
|
476
|
-
b2 = 0;
|
|
477
|
-
} else if (h < 180) {
|
|
478
|
-
r2 = 0;
|
|
479
|
-
g2 = c;
|
|
480
|
-
b2 = x;
|
|
481
|
-
} else if (h < 240) {
|
|
482
|
-
r2 = 0;
|
|
483
|
-
g2 = x;
|
|
484
|
-
b2 = c;
|
|
485
|
-
} else if (h < 300) {
|
|
486
|
-
r2 = x;
|
|
487
|
-
g2 = 0;
|
|
488
|
-
b2 = c;
|
|
489
|
-
} else {
|
|
490
|
-
r2 = c;
|
|
491
|
-
g2 = 0;
|
|
492
|
-
b2 = x;
|
|
493
|
-
}
|
|
494
|
-
const toInt = (n) => Math.round((n + m) * 255);
|
|
495
|
-
return getHex(`rgb(${toInt(r2)}, ${toInt(g2)}, ${toInt(b2)})`) ?? color;
|
|
496
|
-
};
|
|
497
|
-
|
|
498
|
-
// src/utils/getColorRoles.ts
|
|
499
|
-
var getColorRoles = (base, surface, containerAlpha = 0.15) => {
|
|
500
|
-
const container = getBlendedColor(base, surface, containerAlpha);
|
|
501
|
-
return {
|
|
502
|
-
color: base,
|
|
503
|
-
onColor: getTonalColor(base, isDarkColor(base) ? 0.95 : 0.08),
|
|
504
|
-
container,
|
|
505
|
-
onContainer: getTonalColor(base, isDarkColor(container) ? 0.92 : 0.12)
|
|
506
|
-
};
|
|
507
|
-
};
|
|
508
|
-
|
|
509
425
|
// src/useComputedTheme.ts
|
|
510
426
|
function normalizeExplicitColor(value) {
|
|
511
427
|
if (!getRgb(value)) throw new Error(`Invalid color format: ${value}`);
|
|
@@ -517,7 +433,7 @@ var SEMANTIC_BASE_COLORS = {
|
|
|
517
433
|
danger: "#B00020"
|
|
518
434
|
};
|
|
519
435
|
function useComputedTheme(appearance, color, harmony = "split-complementary") {
|
|
520
|
-
const [theme, setTheme] =
|
|
436
|
+
const [theme, setTheme] = useState(null);
|
|
521
437
|
const primaryInput = typeof color === "string" ? color : color.primary;
|
|
522
438
|
const secondaryInput = typeof color === "string" ? null : color.secondary;
|
|
523
439
|
const tertiaryInput = typeof color === "string" ? null : color.tertiary;
|
|
@@ -594,7 +510,7 @@ function useComputedTheme(appearance, color, harmony = "split-complementary") {
|
|
|
594
510
|
}
|
|
595
511
|
|
|
596
512
|
// src/ThemeProvider.tsx
|
|
597
|
-
import { jsx
|
|
513
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
598
514
|
var NavBarContext = createContext2({});
|
|
599
515
|
var useNavBarContext = () => useContext3(NavBarContext);
|
|
600
516
|
var BlurModuleContext = createContext2(void 0);
|
|
@@ -603,11 +519,13 @@ var ReanimatedModuleContext = createContext2(void 0);
|
|
|
603
519
|
var useReanimatedModule = () => useContext3(ReanimatedModuleContext);
|
|
604
520
|
var MaskedViewModuleContext = createContext2(void 0);
|
|
605
521
|
var useMaskedViewModule = () => useContext3(MaskedViewModuleContext);
|
|
606
|
-
|
|
607
|
-
|
|
522
|
+
var StatusBarEnabledContext = createContext2(true);
|
|
523
|
+
var useStatusBarEnabled = () => useContext3(StatusBarEnabledContext);
|
|
524
|
+
function Provider({ children, defaults, expoBlur, fontFamily, initialValue, maskedView, navigationBar, onChange, onNavBarChange, onReady, reanimated, statusBar, statusBarProps, style }) {
|
|
525
|
+
return /* @__PURE__ */ jsx(ThemeSettingsProvider, { initialValue, onChange, children: /* @__PURE__ */ jsx(ThemeProviderBody, { defaults, expoBlur, fontFamily, maskedView, navigationBar, onNavBarChange, onReady, reanimated, statusBar, statusBarProps, style, children }) });
|
|
608
526
|
}
|
|
609
527
|
var ThemeSettingsProvider = themeSettingsProvider;
|
|
610
|
-
function ThemeProviderBody({ children, defaults, expoBlur, fontFamily, maskedView, navigationBar, onNavBarChange, onReady, reanimated, statusBarProps, style }) {
|
|
528
|
+
function ThemeProviderBody({ children, defaults, expoBlur, fontFamily, maskedView, navigationBar, onNavBarChange, onReady, reanimated, statusBar = true, statusBarProps, style }) {
|
|
611
529
|
const { settings } = useThemeSettingsInternal();
|
|
612
530
|
const computedTheme = useComputedTheme(settings.appearance, settings.color, settings.harmony);
|
|
613
531
|
const theme = useMemo(() => {
|
|
@@ -623,15 +541,100 @@ function ThemeProviderBody({ children, defaults, expoBlur, fontFamily, maskedVie
|
|
|
623
541
|
}
|
|
624
542
|
}, [theme, onReady]);
|
|
625
543
|
if (!theme) return null;
|
|
626
|
-
return /* @__PURE__ */
|
|
627
|
-
/* @__PURE__ */
|
|
628
|
-
/* @__PURE__ */
|
|
544
|
+
return /* @__PURE__ */ jsxs(PaperProvider, { theme, children: [
|
|
545
|
+
statusBar && /* @__PURE__ */ jsx(StatusBar, { backgroundColor: theme.colors.background, barStyle: theme.dark ? "light-content" : "dark-content", ...statusBarProps }),
|
|
546
|
+
/* @__PURE__ */ jsx(StatusBarEnabledContext.Provider, { value: statusBar, children: /* @__PURE__ */ jsx(PaperDefaultsContext.Provider, { value: defaults ?? {}, children: /* @__PURE__ */ jsx(NavBarContext.Provider, { value: { navigationBar, onNavBarChange }, children: /* @__PURE__ */ jsx(BlurModuleContext.Provider, { value: expoBlur, children: /* @__PURE__ */ jsx(ReanimatedModuleContext.Provider, { value: reanimated, children: /* @__PURE__ */ jsx(MaskedViewModuleContext.Provider, { value: maskedView, children: /* @__PURE__ */ jsx(Portal.Host, { children: /* @__PURE__ */ jsx(View, { style: [styles.flex, { backgroundColor: theme.colors.background }, style], children }) }) }) }) }) }) }) })
|
|
629
547
|
] });
|
|
630
548
|
}
|
|
631
549
|
var styles = StyleSheet.create({
|
|
632
550
|
flex: { flex: 1 }
|
|
633
551
|
});
|
|
634
552
|
|
|
553
|
+
// src/components/Appbar.tsx
|
|
554
|
+
import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
555
|
+
function Header({ style, ...props }) {
|
|
556
|
+
const defaults = usePaperDefaults();
|
|
557
|
+
const theme = useTheme();
|
|
558
|
+
const statusBar = useStatusBarEnabled();
|
|
559
|
+
return /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
560
|
+
statusBar && /* @__PURE__ */ jsx2(StatusBar2, { backgroundColor: theme.colors.surface, barStyle: theme.dark ? "light-content" : "dark-content" }),
|
|
561
|
+
/* @__PURE__ */ jsx2(PaperAppbar.Header, { ...defaults.AppbarHeader, ...props, style })
|
|
562
|
+
] });
|
|
563
|
+
}
|
|
564
|
+
var Appbar = {
|
|
565
|
+
Header,
|
|
566
|
+
Content: PaperAppbar.Content,
|
|
567
|
+
Action: PaperAppbar.Action,
|
|
568
|
+
BackAction: PaperAppbar.BackAction
|
|
569
|
+
};
|
|
570
|
+
|
|
571
|
+
// src/components/AppearancePicker.tsx
|
|
572
|
+
import { SegmentedButtons } from "react-native-paper";
|
|
573
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
574
|
+
var APPEARANCES = [
|
|
575
|
+
{ label: "System", value: "system" },
|
|
576
|
+
{ label: "Light", value: "light" },
|
|
577
|
+
{ label: "Dark", value: "dark" }
|
|
578
|
+
];
|
|
579
|
+
var DEFAULT_ICONS = {
|
|
580
|
+
system: "monitor",
|
|
581
|
+
light: "white-balance-sunny",
|
|
582
|
+
dark: "weather-night"
|
|
583
|
+
};
|
|
584
|
+
var AppearancePicker = ({ value, onChange, showLabels = true, icons }) => {
|
|
585
|
+
const resolvedIcons = { ...DEFAULT_ICONS, ...icons };
|
|
586
|
+
return /* @__PURE__ */ jsx3(SegmentedButtons, { value, onValueChange: (v) => onChange(v), buttons: APPEARANCES.map((a) => ({ value: a.value, label: showLabels ? a.label : void 0, icon: resolvedIcons[a.value], accessibilityLabel: a.label })) });
|
|
587
|
+
};
|
|
588
|
+
|
|
589
|
+
// src/useThemeSettings.ts
|
|
590
|
+
var useThemeSettings = useThemeSettingsInternal;
|
|
591
|
+
|
|
592
|
+
// src/components/AutoAppearancePicker.tsx
|
|
593
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
594
|
+
var AutoAppearancePicker = (props) => {
|
|
595
|
+
const { settings, set } = useThemeSettings();
|
|
596
|
+
return /* @__PURE__ */ jsx4(AppearancePicker, { ...props, value: settings.appearance, onChange: (appearance) => set({ appearance }) });
|
|
597
|
+
};
|
|
598
|
+
|
|
599
|
+
// src/utils/resolveSeedColor.ts
|
|
600
|
+
var resolveSeedColor = (color) => typeof color === "string" ? color : color.primary;
|
|
601
|
+
|
|
602
|
+
// src/components/PalettePicker.tsx
|
|
603
|
+
import { useState as useState5 } from "react";
|
|
604
|
+
import { StyleSheet as StyleSheet5, TouchableOpacity as TouchableOpacity2, View as View5 } from "react-native";
|
|
605
|
+
import { Icon as Icon2, TouchableRipple as TouchableRipple2, useTheme as useTheme5 } from "react-native-paper";
|
|
606
|
+
|
|
607
|
+
// src/useSwatchGrid.ts
|
|
608
|
+
import { useState as useState2 } from "react";
|
|
609
|
+
var SWATCH_GRID_COLUMNS = 5;
|
|
610
|
+
var SWATCH_GRID_GAP = 12;
|
|
611
|
+
function useSwatchSize(fallback) {
|
|
612
|
+
const [width, setWidth] = useState2(0);
|
|
613
|
+
const onLayout = ({ nativeEvent }) => setWidth(nativeEvent.layout.width);
|
|
614
|
+
const size = width > 0 ? (width - SWATCH_GRID_GAP * (SWATCH_GRID_COLUMNS - 1)) / SWATCH_GRID_COLUMNS : fallback;
|
|
615
|
+
return { onLayout, size };
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
// src/utils/getSwatchContrast.ts
|
|
619
|
+
var RING_WIDTH = 1;
|
|
620
|
+
var SELECTED_RING_WIDTH = 3;
|
|
621
|
+
var getContrastColor = (color) => isDarkColor(color) ? "#ffffff" : "#000000";
|
|
622
|
+
var getSwatchRing = (color, selected, outlineColor) => ({
|
|
623
|
+
borderColor: selected ? getContrastColor(color) : outlineColor,
|
|
624
|
+
borderWidth: selected ? SELECTED_RING_WIDTH : RING_WIDTH
|
|
625
|
+
});
|
|
626
|
+
|
|
627
|
+
// src/components/ColorPicker.tsx
|
|
628
|
+
import { useState as useState4 } from "react";
|
|
629
|
+
import { StyleSheet as StyleSheet4, TouchableOpacity, View as View4 } from "react-native";
|
|
630
|
+
import { Icon, TouchableRipple, useTheme as useTheme4 } from "react-native-paper";
|
|
631
|
+
|
|
632
|
+
// src/components/Dialog.tsx
|
|
633
|
+
import React, { useEffect as useEffect3, useState as useState3 } from "react";
|
|
634
|
+
import { Animated, BackHandler, Easing, Platform as Platform2, Pressable, StyleSheet as StyleSheet3, View as View3 } from "react-native";
|
|
635
|
+
import { Dialog as PaperDialog, Portal as Portal2, useTheme as useTheme3 } from "react-native-paper";
|
|
636
|
+
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
637
|
+
|
|
635
638
|
// src/components/BlurView.tsx
|
|
636
639
|
import { useContext as useContext4 } from "react";
|
|
637
640
|
import { Platform, StyleSheet as StyleSheet2, View as View2 } from "react-native";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rific/auto-paper",
|
|
3
|
-
"version": "0.13.
|
|
3
|
+
"version": "0.13.2",
|
|
4
4
|
"description": "Adaptive react-native-paper theming: triadic palette from a seed color + system/light/dark appearance",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react-native",
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
"@infinitetoken/tsconfig": "^0.5.0",
|
|
74
74
|
"@react-native-masked-view/masked-view": "^0.3.2",
|
|
75
75
|
"@reduxjs/toolkit": "^2.0.0",
|
|
76
|
-
"@rific/core": "^0.
|
|
76
|
+
"@rific/core": "^0.3.0",
|
|
77
77
|
"@testing-library/dom": "^10.4.1",
|
|
78
78
|
"@testing-library/react": "^16.0.0",
|
|
79
79
|
"@types/jest": "^30.0.0",
|
|
@@ -81,14 +81,14 @@
|
|
|
81
81
|
"eslint": "^9.39.4",
|
|
82
82
|
"eslint-plugin-react-hooks": "^7.1.1",
|
|
83
83
|
"eslint-plugin-react-native": "^5.0.0",
|
|
84
|
-
"expo-blur": "^
|
|
85
|
-
"expo-navigation-bar": "^
|
|
84
|
+
"expo-blur": "^57.0.3",
|
|
85
|
+
"expo-navigation-bar": "^57.0.3",
|
|
86
86
|
"jest": "^30.0.0",
|
|
87
87
|
"jest-environment-jsdom": "^30.0.0",
|
|
88
88
|
"prettier": "^3.8.3",
|
|
89
89
|
"react": "^19.0.0",
|
|
90
90
|
"react-dom": "^19.0.0",
|
|
91
|
-
"react-native": "^0.
|
|
91
|
+
"react-native": "^0.86.3",
|
|
92
92
|
"react-native-paper": "^5.12.0",
|
|
93
93
|
"react-native-safe-area-context": "^5.5.2",
|
|
94
94
|
"tsup": "^8.0.0",
|
package/src/ThemeProvider.tsx
CHANGED
|
@@ -28,6 +28,10 @@ export const useReanimatedModule = () => useContext(ReanimatedModuleContext)
|
|
|
28
28
|
const MaskedViewModuleContext = createContext<MaskedViewModule | undefined>(undefined)
|
|
29
29
|
export const useMaskedViewModule = () => useContext(MaskedViewModuleContext)
|
|
30
30
|
|
|
31
|
+
// Whether this package's own components (Provider, Appbar.Header) render a React Native StatusBar - see `statusBar` below.
|
|
32
|
+
const StatusBarEnabledContext = createContext(true)
|
|
33
|
+
export const useStatusBarEnabled = () => useContext(StatusBarEnabledContext)
|
|
34
|
+
|
|
31
35
|
export type ProviderProps = {
|
|
32
36
|
children: ReactNode
|
|
33
37
|
defaults?: PaperDefaults
|
|
@@ -49,6 +53,11 @@ export type ProviderProps = {
|
|
|
49
53
|
onReady?: () => void
|
|
50
54
|
/** Injects react-native-reanimated so `<Dialog animatedStyle={...}>` (e.g. from `useAnimatedStyle()`) can animate its card on the UI thread. Pass `import Reanimated from 'react-native-reanimated'`; omit to keep Dialog's `animatedStyle` prop a no-op. */
|
|
51
55
|
reanimated?: ReanimatedModule
|
|
56
|
+
/** Renders React Native's `StatusBar`, styled from the theme (here and in `Appbar.Header`). Default true. Pass false
|
|
57
|
+
* for an app that sets the status bar per screen instead (react-native-screens' `statusBarStyle`/`statusBarHidden`
|
|
58
|
+
* screen options, with `UIViewControllerBasedStatusBarAppearance` on): there React Native's StatusBar logs an error,
|
|
59
|
+
* and an app linked against the iOS 27 SDK can't set the status bar app-wide at all, which is the only way it works on iOS. */
|
|
60
|
+
statusBar?: boolean
|
|
52
61
|
statusBarProps?: StatusBarProps
|
|
53
62
|
style?: StyleProp<ViewStyle>
|
|
54
63
|
}
|
|
@@ -59,10 +68,10 @@ export type ProviderProps = {
|
|
|
59
68
|
// wrapper for the settings half; ThemeProviderBody (an actual descendant, reached via
|
|
60
69
|
// useThemeSettingsInternal()) does all the theme-computation/gating work the single combined
|
|
61
70
|
// component used to do directly.
|
|
62
|
-
export function Provider({ children, defaults, expoBlur, fontFamily, initialValue, maskedView, navigationBar, onChange, onNavBarChange, onReady, reanimated, statusBarProps, style }: ProviderProps) {
|
|
71
|
+
export function Provider({ children, defaults, expoBlur, fontFamily, initialValue, maskedView, navigationBar, onChange, onNavBarChange, onReady, reanimated, statusBar, statusBarProps, style }: ProviderProps) {
|
|
63
72
|
return (
|
|
64
73
|
<ThemeSettingsProvider initialValue={initialValue} onChange={onChange}>
|
|
65
|
-
<ThemeProviderBody defaults={defaults} expoBlur={expoBlur} fontFamily={fontFamily} maskedView={maskedView} navigationBar={navigationBar} onNavBarChange={onNavBarChange} onReady={onReady} reanimated={reanimated} statusBarProps={statusBarProps} style={style}>
|
|
74
|
+
<ThemeProviderBody defaults={defaults} expoBlur={expoBlur} fontFamily={fontFamily} maskedView={maskedView} navigationBar={navigationBar} onNavBarChange={onNavBarChange} onReady={onReady} reanimated={reanimated} statusBar={statusBar} statusBarProps={statusBarProps} style={style}>
|
|
66
75
|
{children}
|
|
67
76
|
</ThemeProviderBody>
|
|
68
77
|
</ThemeSettingsProvider>
|
|
@@ -73,7 +82,7 @@ const ThemeSettingsProvider = themeSettingsProvider
|
|
|
73
82
|
|
|
74
83
|
type ThemeProviderBodyProps = Omit<ProviderProps, 'initialValue' | 'onChange'>
|
|
75
84
|
|
|
76
|
-
function ThemeProviderBody({ children, defaults, expoBlur, fontFamily, maskedView, navigationBar, onNavBarChange, onReady, reanimated, statusBarProps, style }: ThemeProviderBodyProps) {
|
|
85
|
+
function ThemeProviderBody({ children, defaults, expoBlur, fontFamily, maskedView, navigationBar, onNavBarChange, onReady, reanimated, statusBar = true, statusBarProps, style }: ThemeProviderBodyProps) {
|
|
77
86
|
const { settings } = useThemeSettingsInternal()
|
|
78
87
|
|
|
79
88
|
const computedTheme = useComputedTheme(settings.appearance, settings.color, settings.harmony)
|
|
@@ -100,28 +109,30 @@ function ThemeProviderBody({ children, defaults, expoBlur, fontFamily, maskedVie
|
|
|
100
109
|
|
|
101
110
|
return (
|
|
102
111
|
<PaperProvider theme={theme}>
|
|
103
|
-
<StatusBar backgroundColor={theme.colors.background} barStyle={theme.dark ? 'light-content' : 'dark-content'} {...statusBarProps} />
|
|
104
|
-
<
|
|
105
|
-
<
|
|
106
|
-
<
|
|
107
|
-
<
|
|
108
|
-
<
|
|
109
|
-
{
|
|
112
|
+
{statusBar && <StatusBar backgroundColor={theme.colors.background} barStyle={theme.dark ? 'light-content' : 'dark-content'} {...statusBarProps} />}
|
|
113
|
+
<StatusBarEnabledContext.Provider value={statusBar}>
|
|
114
|
+
<PaperDefaultsContext.Provider value={defaults ?? {}}>
|
|
115
|
+
<NavBarContext.Provider value={{ navigationBar, onNavBarChange }}>
|
|
116
|
+
<BlurModuleContext.Provider value={expoBlur}>
|
|
117
|
+
<ReanimatedModuleContext.Provider value={reanimated}>
|
|
118
|
+
<MaskedViewModuleContext.Provider value={maskedView}>
|
|
119
|
+
{/* PaperProvider's own PortalHost sits above every context provided here, and Paper's
|
|
110
120
|
Portal only carries its theme and settings across, so anything rendered through a
|
|
111
121
|
Portal (this package's Dialog, Paper's Menu and Tooltip, or anything an app wraps
|
|
112
|
-
in <Portal> itself) would otherwise lose expoBlur, reanimated, maskedView, defaults and the nav
|
|
122
|
+
in <Portal> itself) would otherwise lose expoBlur, reanimated, maskedView, defaults, statusBar and the nav
|
|
113
123
|
bar hooks: Dialog/Menu's blur silently fell back to solid. This nested host is the
|
|
114
124
|
nearest one for every Portal under Provider, so their content mounts inside all of
|
|
115
125
|
these contexts instead. It wraps the styled View, not the other way round, so
|
|
116
126
|
portals still cover the full area regardless of `style`. */}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
127
|
+
<Portal.Host>
|
|
128
|
+
<View style={[styles.flex, { backgroundColor: theme.colors.background }, style]}>{children}</View>
|
|
129
|
+
</Portal.Host>
|
|
130
|
+
</MaskedViewModuleContext.Provider>
|
|
131
|
+
</ReanimatedModuleContext.Provider>
|
|
132
|
+
</BlurModuleContext.Provider>
|
|
133
|
+
</NavBarContext.Provider>
|
|
134
|
+
</PaperDefaultsContext.Provider>
|
|
135
|
+
</StatusBarEnabledContext.Provider>
|
|
125
136
|
</PaperProvider>
|
|
126
137
|
)
|
|
127
138
|
}
|
|
@@ -2,13 +2,16 @@ import { StatusBar } from 'react-native'
|
|
|
2
2
|
import { Appbar as PaperAppbar, type AppbarHeaderProps, useTheme } from 'react-native-paper'
|
|
3
3
|
|
|
4
4
|
import { usePaperDefaults } from '../PaperDefaultsContext'
|
|
5
|
+
import { useStatusBarEnabled } from '../ThemeProvider'
|
|
5
6
|
|
|
6
7
|
function Header({ style, ...props }: AppbarHeaderProps) {
|
|
7
8
|
const defaults = usePaperDefaults()
|
|
8
9
|
const theme = useTheme()
|
|
10
|
+
// Off under <Provider statusBar={false}>, an app that sets the status bar per screen.
|
|
11
|
+
const statusBar = useStatusBarEnabled()
|
|
9
12
|
return (
|
|
10
13
|
<>
|
|
11
|
-
<StatusBar backgroundColor={theme.colors.surface} barStyle={theme.dark ? 'light-content' : 'dark-content'} />
|
|
14
|
+
{statusBar && <StatusBar backgroundColor={theme.colors.surface} barStyle={theme.dark ? 'light-content' : 'dark-content'} />}
|
|
12
15
|
<PaperAppbar.Header {...defaults.AppbarHeader} {...props} style={style} />
|
|
13
16
|
</>
|
|
14
17
|
)
|
|
@@ -91,9 +91,10 @@ export function FadingScrollView({ style, fadeHeight = SCROLL_FADE_HEIGHT, onScr
|
|
|
91
91
|
)
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
-
// React Native's native linear gradient (0.77+, the New Architecture; the fleet's apps are on 0.86)
|
|
95
|
-
//
|
|
96
|
-
// content, transparent
|
|
94
|
+
// React Native's native linear gradient (0.77+, the New Architecture; the fleet's apps are on 0.86). Cast
|
|
95
|
+
// because the peer floor is still 0.76, whose types don't have it, and an app resolves this source file
|
|
96
|
+
// against its own React Native types. A mask's alpha is what shows: opaque keeps the content, transparent
|
|
97
|
+
// hides it.
|
|
97
98
|
const FADE_IN_BAND = { experimental_backgroundImage: 'linear-gradient(transparent, #000)' } as unknown as ViewStyle
|
|
98
99
|
const FADE_OUT_BAND = { experimental_backgroundImage: 'linear-gradient(#000, transparent)' } as unknown as ViewStyle
|
|
99
100
|
|