@rific/auto-paper 0.9.2 → 0.9.3
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 +24 -2
- package/dist/index.d.mts +8 -3
- package/dist/index.d.ts +8 -3
- package/dist/index.js +65 -69
- package/dist/index.mjs +60 -64
- package/package.json +1 -1
- package/src/components/HarmonyPicker.tsx +44 -34
- package/src/components/PalettePicker.tsx +14 -1
package/README.md
CHANGED
|
@@ -327,6 +327,27 @@ function SettingsScreen() {
|
|
|
327
327
|
| `showLabels` | `boolean` | Set `false` for icon-only segments, useful in tight layouts where "System" would truncate. `accessibilityLabel` is always set regardless, so screen readers still announce the full word. Defaults to `true` |
|
|
328
328
|
| `icons` | `AppearanceIcons` | Per-value icon overrides: pass just the ones you want to change (e.g. `{ system: 'theme-light-dark' }`), merged over the defaults (`monitor` / `white-balance-sunny` / `weather-night`) |
|
|
329
329
|
|
|
330
|
+
### `HarmonyPicker`
|
|
331
|
+
|
|
332
|
+
Switches between the 6 `ColorHarmony` modes (`triadic`, `split-complementary`, `analogous`, `square`, `complementary`, `double-split`) via `react-native-paper`'s `SegmentedButtons`. `PalettePicker` can embed this same picker in its own dialog instead of standing alone — see `onHarmonyChange` below.
|
|
333
|
+
|
|
334
|
+
```tsx
|
|
335
|
+
import { HarmonyPicker, useThemeSettings } from '@rific/auto-paper'
|
|
336
|
+
|
|
337
|
+
function SettingsScreen() {
|
|
338
|
+
const { settings: { harmony }, set } = useThemeSettings()
|
|
339
|
+
return <HarmonyPicker value={harmony} onChange={(h) => set({ harmony: h })} showLabels={false} />
|
|
340
|
+
}
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
| Prop | Type | Description |
|
|
344
|
+
|---|---|---|
|
|
345
|
+
| `value` | `ColorHarmony` | Currently selected harmony |
|
|
346
|
+
| `onChange` | `(harmony: ColorHarmony) => void` | Called when a segment is tapped |
|
|
347
|
+
| `showLabels` | `boolean` | Set `false` for icon-only segments. Also lowers Paper's default 76px-per-segment minimum width down to 0, so all 6 segments can always shrink to fit — however narrow the container, they resize down together instead of overflowing it. `accessibilityLabel` is always set regardless, so screen readers still announce the full harmony name either way. Defaults to `true` |
|
|
348
|
+
| `checkedColor` / `uncheckedColor` | `string` | Icon color for the checked and unchecked segments, matching Paper's own segmented-button vocabulary |
|
|
349
|
+
| `checkedContainerColor` / `uncheckedContainerColor` | `string` | Fill color for the checked and unchecked segments. Omit any of these four color props to fall back to Paper's default segmented-button styling |
|
|
350
|
+
|
|
330
351
|
### `Appbar`
|
|
331
352
|
|
|
332
353
|
A thin wrapper around `react-native-paper`'s `Appbar` that automatically syncs `StatusBar` background color and bar style to the current theme surface color.
|
|
@@ -432,14 +453,14 @@ import { IconButton } from '@rific/auto-paper'
|
|
|
432
453
|
|
|
433
454
|
### `PalettePicker`
|
|
434
455
|
|
|
435
|
-
Like `ColorPicker`, but each swatch (and the trigger itself) renders the seed's full triadic palette as a 3-wedge pie instead of a flat color, so you see the whole result before picking.
|
|
456
|
+
Like `ColorPicker`, but each swatch (and the trigger itself) renders the seed's full triadic palette as a 3-wedge pie instead of a flat color, so you see the whole result before picking. Pass `onHarmonyChange` to fold a compact, icon-only `HarmonyPicker` row into the same dialog, above the swatch grid — every swatch's own pie reshapes live as harmony changes, so there's one place to tune both instead of two separate controls. Omit it and this renders exactly as a plain color-only picker always has.
|
|
436
457
|
|
|
437
458
|
```tsx
|
|
438
459
|
import { PalettePicker, useThemeSettings } from '@rific/auto-paper'
|
|
439
460
|
|
|
440
461
|
function SettingsScreen() {
|
|
441
462
|
const { settings: { color, harmony }, set } = useThemeSettings()
|
|
442
|
-
return <PalettePicker value={color} harmony={harmony} onChange={(c) => set({ color: c })} />
|
|
463
|
+
return <PalettePicker value={color} harmony={harmony} onChange={(c) => set({ color: c })} onHarmonyChange={(h) => set({ harmony: h })} />
|
|
443
464
|
}
|
|
444
465
|
```
|
|
445
466
|
|
|
@@ -448,6 +469,7 @@ function SettingsScreen() {
|
|
|
448
469
|
| `value` | `string` | Currently selected seed color |
|
|
449
470
|
| `onChange` | `(color: string) => void` | Called with the seed color when a swatch is tapped |
|
|
450
471
|
| `harmony` | `ColorHarmony` | Harmony used to compute each swatch's preview palette. Defaults to `'split-complementary'` |
|
|
472
|
+
| `onHarmonyChange` | `(harmony: ColorHarmony) => void` | Opt-in: passing this renders the `HarmonyPicker` row inside the dialog. Gated on the callback itself rather than a separate boolean, so there's no way to pass one without the other |
|
|
451
473
|
| `weights` | `PaletteWeights` | Relative size of each wedge: `{ primary, secondary, tertiary }`. Defaults to `{ primary: 2, secondary: 1, tertiary: 1 }` (primary half the pie, secondary/tertiary a quarter each). Values are normalized, so any positive ratio works; keep each share at or under half the total so its wedge stays a single slice |
|
|
452
474
|
| `colors` | `SeedColor[]` | Seed color swatches to offer. Defaults to the same set as `ColorPicker` |
|
|
453
475
|
| `blur` | `boolean` | Overrides the ambient blur setting for this component's dialog |
|
package/dist/index.d.mts
CHANGED
|
@@ -153,9 +153,13 @@ declare function FAB(props: FABProps): react.JSX.Element;
|
|
|
153
153
|
type Props$1 = {
|
|
154
154
|
value: ColorHarmony;
|
|
155
155
|
onChange: (harmony: ColorHarmony) => void;
|
|
156
|
-
|
|
156
|
+
showLabels?: boolean;
|
|
157
|
+
checkedColor?: string;
|
|
158
|
+
uncheckedColor?: string;
|
|
159
|
+
checkedContainerColor?: string;
|
|
160
|
+
uncheckedContainerColor?: string;
|
|
157
161
|
};
|
|
158
|
-
declare const HarmonyPicker: ({ value, onChange,
|
|
162
|
+
declare const HarmonyPicker: ({ value, onChange, showLabels, checkedColor, uncheckedColor, checkedContainerColor, uncheckedContainerColor }: Props$1) => react.JSX.Element;
|
|
159
163
|
|
|
160
164
|
type IconButtonProps = IconButtonProps$1 & {
|
|
161
165
|
variant?: 'primary' | 'secondary' | 'tertiary' | 'surface';
|
|
@@ -178,11 +182,12 @@ type Props = {
|
|
|
178
182
|
value: string;
|
|
179
183
|
onChange: (color: string) => void;
|
|
180
184
|
harmony?: ColorHarmony;
|
|
185
|
+
onHarmonyChange?: (harmony: ColorHarmony) => void;
|
|
181
186
|
weights?: PaletteWeights;
|
|
182
187
|
colors?: SeedColor[];
|
|
183
188
|
blur?: boolean;
|
|
184
189
|
};
|
|
185
|
-
declare const PalettePicker: ({ value, onChange, harmony, weights, colors, blur: blurProp }: Props) => react.JSX.Element;
|
|
190
|
+
declare const PalettePicker: ({ value, onChange, harmony, onHarmonyChange, weights, colors, blur: blurProp }: Props) => react.JSX.Element;
|
|
186
191
|
|
|
187
192
|
declare const TextInput: react.ForwardRefExoticComponent<Omit<TextInputProps, "ref"> & react.RefAttributes<TextInput$1>>;
|
|
188
193
|
|
package/dist/index.d.ts
CHANGED
|
@@ -153,9 +153,13 @@ declare function FAB(props: FABProps): react.JSX.Element;
|
|
|
153
153
|
type Props$1 = {
|
|
154
154
|
value: ColorHarmony;
|
|
155
155
|
onChange: (harmony: ColorHarmony) => void;
|
|
156
|
-
|
|
156
|
+
showLabels?: boolean;
|
|
157
|
+
checkedColor?: string;
|
|
158
|
+
uncheckedColor?: string;
|
|
159
|
+
checkedContainerColor?: string;
|
|
160
|
+
uncheckedContainerColor?: string;
|
|
157
161
|
};
|
|
158
|
-
declare const HarmonyPicker: ({ value, onChange,
|
|
162
|
+
declare const HarmonyPicker: ({ value, onChange, showLabels, checkedColor, uncheckedColor, checkedContainerColor, uncheckedContainerColor }: Props$1) => react.JSX.Element;
|
|
159
163
|
|
|
160
164
|
type IconButtonProps = IconButtonProps$1 & {
|
|
161
165
|
variant?: 'primary' | 'secondary' | 'tertiary' | 'surface';
|
|
@@ -178,11 +182,12 @@ type Props = {
|
|
|
178
182
|
value: string;
|
|
179
183
|
onChange: (color: string) => void;
|
|
180
184
|
harmony?: ColorHarmony;
|
|
185
|
+
onHarmonyChange?: (harmony: ColorHarmony) => void;
|
|
181
186
|
weights?: PaletteWeights;
|
|
182
187
|
colors?: SeedColor[];
|
|
183
188
|
blur?: boolean;
|
|
184
189
|
};
|
|
185
|
-
declare const PalettePicker: ({ value, onChange, harmony, weights, colors, blur: blurProp }: Props) => react.JSX.Element;
|
|
190
|
+
declare const PalettePicker: ({ value, onChange, harmony, onHarmonyChange, weights, colors, blur: blurProp }: Props) => react.JSX.Element;
|
|
186
191
|
|
|
187
192
|
declare const TextInput: react.ForwardRefExoticComponent<Omit<TextInputProps, "ref"> & react.RefAttributes<TextInput$1>>;
|
|
188
193
|
|
package/dist/index.js
CHANGED
|
@@ -899,26 +899,8 @@ function FAB(props) {
|
|
|
899
899
|
}
|
|
900
900
|
|
|
901
901
|
// src/components/HarmonyPicker.tsx
|
|
902
|
-
var import_react11 = require("react");
|
|
903
|
-
var import_react_native_paper13 = require("react-native-paper");
|
|
904
|
-
|
|
905
|
-
// src/components/Menu.tsx
|
|
906
|
-
var import_react_native8 = require("react-native");
|
|
907
902
|
var import_react_native_paper12 = require("react-native-paper");
|
|
908
903
|
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
909
|
-
var MenuComponent = ({ blur: blurProp, children, ...props }) => {
|
|
910
|
-
const blur = useBlur(blurProp);
|
|
911
|
-
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_react_native_paper12.Menu, { ...props, contentStyle: [styles4.content, props.contentStyle], children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(BlurView, { blur, children }) });
|
|
912
|
-
};
|
|
913
|
-
var Menu = Object.assign(MenuComponent, {
|
|
914
|
-
Item: import_react_native_paper12.Menu.Item
|
|
915
|
-
});
|
|
916
|
-
var styles4 = import_react_native8.StyleSheet.create({
|
|
917
|
-
content: { backgroundColor: "transparent", paddingVertical: 0 }
|
|
918
|
-
});
|
|
919
|
-
|
|
920
|
-
// src/components/HarmonyPicker.tsx
|
|
921
|
-
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
922
904
|
var HARMONIES = [
|
|
923
905
|
{ label: "Triadic", value: "triadic", icon: "triangle-outline" },
|
|
924
906
|
{ label: "Split", value: "split-complementary", icon: "arrow-split-vertical" },
|
|
@@ -927,46 +909,56 @@ var HARMONIES = [
|
|
|
927
909
|
{ label: "Complement", value: "complementary", icon: "circle-half-full" },
|
|
928
910
|
{ label: "Dbl Split", value: "double-split", icon: "arrow-expand-horizontal" }
|
|
929
911
|
];
|
|
930
|
-
var
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
{
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
h.value
|
|
952
|
-
))
|
|
953
|
-
}
|
|
954
|
-
);
|
|
955
|
-
};
|
|
912
|
+
var COMPACT_MIN_WIDTH = 0;
|
|
913
|
+
var HarmonyPicker = ({ value, onChange, showLabels = true, checkedColor, uncheckedColor, checkedContainerColor, uncheckedContainerColor }) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
914
|
+
import_react_native_paper12.SegmentedButtons,
|
|
915
|
+
{
|
|
916
|
+
value,
|
|
917
|
+
onValueChange: (v) => onChange(v),
|
|
918
|
+
buttons: HARMONIES.map((h) => {
|
|
919
|
+
const checked = h.value === value;
|
|
920
|
+
const containerColor = checked ? checkedContainerColor : uncheckedContainerColor;
|
|
921
|
+
return {
|
|
922
|
+
value: h.value,
|
|
923
|
+
icon: h.icon,
|
|
924
|
+
label: showLabels ? h.label : void 0,
|
|
925
|
+
accessibilityLabel: h.label,
|
|
926
|
+
checkedColor,
|
|
927
|
+
uncheckedColor,
|
|
928
|
+
style: [showLabels ? void 0 : { minWidth: COMPACT_MIN_WIDTH }, containerColor ? { backgroundColor: containerColor } : void 0]
|
|
929
|
+
};
|
|
930
|
+
})
|
|
931
|
+
}
|
|
932
|
+
);
|
|
956
933
|
|
|
957
934
|
// src/components/IconButton.tsx
|
|
958
|
-
var
|
|
959
|
-
var
|
|
935
|
+
var import_react_native_paper13 = require("react-native-paper");
|
|
936
|
+
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
960
937
|
function IconButton({ variant, iconColor, containerColor, ...props }) {
|
|
961
938
|
const defaults = usePaperDefaults();
|
|
962
|
-
const { colors } = (0,
|
|
939
|
+
const { colors } = (0, import_react_native_paper13.useTheme)();
|
|
963
940
|
const variantContainerColor = variant === "primary" ? colors.primaryContainer : variant === "secondary" ? colors.secondaryContainer : variant === "tertiary" ? colors.tertiaryContainer : variant === "surface" ? colors.surface : void 0;
|
|
964
941
|
const variantIconColor = variant === "primary" ? colors.onPrimaryContainer : variant === "secondary" ? colors.onSecondaryContainer : variant === "tertiary" ? colors.onTertiaryContainer : variant === "surface" ? colors.onSurface : void 0;
|
|
965
|
-
return /* @__PURE__ */ (0,
|
|
942
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native_paper13.IconButton, { ...defaults.IconButton, containerColor: containerColor ?? variantContainerColor, iconColor: iconColor ?? variantIconColor, ...props });
|
|
966
943
|
}
|
|
967
944
|
|
|
945
|
+
// src/components/Menu.tsx
|
|
946
|
+
var import_react_native8 = require("react-native");
|
|
947
|
+
var import_react_native_paper14 = require("react-native-paper");
|
|
948
|
+
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
949
|
+
var MenuComponent = ({ blur: blurProp, children, ...props }) => {
|
|
950
|
+
const blur = useBlur(blurProp);
|
|
951
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react_native_paper14.Menu, { ...props, contentStyle: [styles4.content, props.contentStyle], children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(BlurView, { blur, children }) });
|
|
952
|
+
};
|
|
953
|
+
var Menu = Object.assign(MenuComponent, {
|
|
954
|
+
Item: import_react_native_paper14.Menu.Item
|
|
955
|
+
});
|
|
956
|
+
var styles4 = import_react_native8.StyleSheet.create({
|
|
957
|
+
content: { backgroundColor: "transparent", paddingVertical: 0 }
|
|
958
|
+
});
|
|
959
|
+
|
|
968
960
|
// src/components/PalettePicker.tsx
|
|
969
|
-
var
|
|
961
|
+
var import_react11 = require("react");
|
|
970
962
|
var import_react_native9 = require("react-native");
|
|
971
963
|
var import_react_native_paper15 = require("react-native-paper");
|
|
972
964
|
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
@@ -984,35 +976,39 @@ var buildWedges = (weights) => {
|
|
|
984
976
|
];
|
|
985
977
|
};
|
|
986
978
|
var Pie = ({ palette, weights = defaultWeights, size = SIZE2 }) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native9.View, { style: [styles5.pie, { borderRadius: size / 2, height: size, width: size }], children: buildWedges(weights).map(({ key, rotate, share }) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native9.View, { style: [styles5.rotator, { transform: [{ rotate: `${rotate}deg` }] }], children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native9.View, { style: styles5.halfMask, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native9.View, { style: [styles5.innerRotator, { transform: [{ rotate: `${share - 180}deg` }] }], children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native9.View, { style: [styles5.wedgeFill, { backgroundColor: palette[key] }] }) }) }) }, key)) });
|
|
987
|
-
var PalettePicker = ({ value, onChange, harmony = "split-complementary", weights, colors = defaultColors, blur: blurProp }) => {
|
|
979
|
+
var PalettePicker = ({ value, onChange, harmony = "split-complementary", onHarmonyChange, weights, colors = defaultColors, blur: blurProp }) => {
|
|
988
980
|
const blur = useBlur(blurProp);
|
|
989
981
|
const { colors: themeColors } = (0, import_react_native_paper15.useTheme)();
|
|
990
|
-
const [open, setOpen] = (0,
|
|
982
|
+
const [open, setOpen] = (0, import_react11.useState)(false);
|
|
991
983
|
const { onLayout, size } = useSwatchSize(SIZE2);
|
|
992
984
|
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
|
|
993
985
|
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native_paper15.TouchableRipple, { onPress: () => setOpen(true), style: styles5.trigger, borderless: true, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_react_native9.View, { style: styles5.triggerContent, children: [
|
|
994
986
|
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Pie, { palette: getTriadicPalette(value, harmony), weights }),
|
|
995
987
|
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native9.View, { pointerEvents: "none", style: [styles5.triggerIcon, { borderRadius: SIZE2 / 2 }, getSwatchRing(value, false, themeColors.outlineVariant)], children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native_paper15.Icon, { source: "palette", size: 20, color: getContrastColor(value) }) })
|
|
996
988
|
] }) }),
|
|
997
|
-
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Dialog, { blur, visible: open, onDismiss: () => setOpen(false), children: /* @__PURE__ */ (0, import_jsx_runtime14.
|
|
998
|
-
import_react_native9.
|
|
999
|
-
{
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
989
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Dialog, { blur, visible: open, onDismiss: () => setOpen(false), children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(Dialog.Content, { children: [
|
|
990
|
+
onHarmonyChange && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native9.View, { style: styles5.harmonyRow, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(HarmonyPicker, { value: harmony, onChange: onHarmonyChange, showLabels: false }) }),
|
|
991
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native9.View, { style: styles5.swatches, onLayout, children: colors.map(({ value: hex }) => /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
|
|
992
|
+
import_react_native9.TouchableOpacity,
|
|
993
|
+
{
|
|
994
|
+
onPress: () => {
|
|
995
|
+
onChange(hex);
|
|
996
|
+
setOpen(false);
|
|
997
|
+
},
|
|
998
|
+
style: [styles5.swatch, { height: size, width: size }],
|
|
999
|
+
children: [
|
|
1000
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Pie, { palette: getTriadicPalette(hex, harmony), weights, size }),
|
|
1001
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native9.View, { pointerEvents: "none", style: [styles5.ring, { borderRadius: size / 2 }, getSwatchRing(hex, value === hex, themeColors.outlineVariant)], children: value === hex && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native_paper15.Icon, { source: "check", size: 20, color: getContrastColor(hex) }) })
|
|
1002
|
+
]
|
|
1003
1003
|
},
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native9.View, { pointerEvents: "none", style: [styles5.ring, { borderRadius: size / 2 }, getSwatchRing(hex, value === hex, themeColors.outlineVariant)], children: value === hex && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native_paper15.Icon, { source: "check", size: 20, color: getContrastColor(hex) }) })
|
|
1008
|
-
]
|
|
1009
|
-
},
|
|
1010
|
-
hex
|
|
1011
|
-
)) }) }) })
|
|
1004
|
+
hex
|
|
1005
|
+
)) })
|
|
1006
|
+
] }) })
|
|
1012
1007
|
] });
|
|
1013
1008
|
};
|
|
1014
1009
|
var styles5 = import_react_native9.StyleSheet.create({
|
|
1015
1010
|
halfMask: { height: "100%", left: "50%", overflow: "hidden", position: "absolute", top: 0, width: "50%" },
|
|
1011
|
+
harmonyRow: { marginBottom: 4 },
|
|
1016
1012
|
innerRotator: { height: "100%", left: "-100%", overflow: "hidden", position: "absolute", top: 0, width: "200%" },
|
|
1017
1013
|
pie: { overflow: "hidden" },
|
|
1018
1014
|
rotator: { height: "100%", left: 0, overflow: "hidden", position: "absolute", top: 0, width: "100%" },
|
|
@@ -1026,10 +1022,10 @@ var styles5 = import_react_native9.StyleSheet.create({
|
|
|
1026
1022
|
});
|
|
1027
1023
|
|
|
1028
1024
|
// src/components/TextInput.tsx
|
|
1029
|
-
var
|
|
1025
|
+
var import_react12 = require("react");
|
|
1030
1026
|
var import_react_native_paper16 = require("react-native-paper");
|
|
1031
1027
|
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
1032
|
-
var TextInput = (0,
|
|
1028
|
+
var TextInput = (0, import_react12.forwardRef)(function TextInput2(props, ref) {
|
|
1033
1029
|
const defaults = usePaperDefaults();
|
|
1034
1030
|
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_react_native_paper16.TextInput, { ref, ...defaults.TextInput, ...props });
|
|
1035
1031
|
});
|
|
@@ -1076,8 +1072,8 @@ var import_react_native_paper17 = require("react-native-paper");
|
|
|
1076
1072
|
var useAutoPaperTheme = () => (0, import_react_native_paper17.useTheme)();
|
|
1077
1073
|
|
|
1078
1074
|
// src/useThemeSettings.ts
|
|
1079
|
-
var
|
|
1080
|
-
var useThemeSettings = () => (0,
|
|
1075
|
+
var import_react13 = require("react");
|
|
1076
|
+
var useThemeSettings = () => (0, import_react13.useContext)(ThemeSettingsContext);
|
|
1081
1077
|
|
|
1082
1078
|
// src/utils/getThirdColor.ts
|
|
1083
1079
|
function rgbToHsl(r, g, b) {
|
package/dist/index.mjs
CHANGED
|
@@ -820,26 +820,8 @@ function FAB(props) {
|
|
|
820
820
|
}
|
|
821
821
|
|
|
822
822
|
// src/components/HarmonyPicker.tsx
|
|
823
|
-
import {
|
|
824
|
-
import { Button as Button2 } from "react-native-paper";
|
|
825
|
-
|
|
826
|
-
// src/components/Menu.tsx
|
|
827
|
-
import { StyleSheet as StyleSheet5 } from "react-native";
|
|
828
|
-
import { Menu as PaperMenu } from "react-native-paper";
|
|
823
|
+
import { SegmentedButtons as SegmentedButtons2 } from "react-native-paper";
|
|
829
824
|
import { jsx as jsx11 } from "react/jsx-runtime";
|
|
830
|
-
var MenuComponent = ({ blur: blurProp, children, ...props }) => {
|
|
831
|
-
const blur = useBlur(blurProp);
|
|
832
|
-
return /* @__PURE__ */ jsx11(PaperMenu, { ...props, contentStyle: [styles4.content, props.contentStyle], children: /* @__PURE__ */ jsx11(BlurView, { blur, children }) });
|
|
833
|
-
};
|
|
834
|
-
var Menu = Object.assign(MenuComponent, {
|
|
835
|
-
Item: PaperMenu.Item
|
|
836
|
-
});
|
|
837
|
-
var styles4 = StyleSheet5.create({
|
|
838
|
-
content: { backgroundColor: "transparent", paddingVertical: 0 }
|
|
839
|
-
});
|
|
840
|
-
|
|
841
|
-
// src/components/HarmonyPicker.tsx
|
|
842
|
-
import { jsx as jsx12 } from "react/jsx-runtime";
|
|
843
825
|
var HARMONIES = [
|
|
844
826
|
{ label: "Triadic", value: "triadic", icon: "triangle-outline" },
|
|
845
827
|
{ label: "Split", value: "split-complementary", icon: "arrow-split-vertical" },
|
|
@@ -848,46 +830,56 @@ var HARMONIES = [
|
|
|
848
830
|
{ label: "Complement", value: "complementary", icon: "circle-half-full" },
|
|
849
831
|
{ label: "Dbl Split", value: "double-split", icon: "arrow-expand-horizontal" }
|
|
850
832
|
];
|
|
851
|
-
var
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
{
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
h.value
|
|
873
|
-
))
|
|
874
|
-
}
|
|
875
|
-
);
|
|
876
|
-
};
|
|
833
|
+
var COMPACT_MIN_WIDTH = 0;
|
|
834
|
+
var HarmonyPicker = ({ value, onChange, showLabels = true, checkedColor, uncheckedColor, checkedContainerColor, uncheckedContainerColor }) => /* @__PURE__ */ jsx11(
|
|
835
|
+
SegmentedButtons2,
|
|
836
|
+
{
|
|
837
|
+
value,
|
|
838
|
+
onValueChange: (v) => onChange(v),
|
|
839
|
+
buttons: HARMONIES.map((h) => {
|
|
840
|
+
const checked = h.value === value;
|
|
841
|
+
const containerColor = checked ? checkedContainerColor : uncheckedContainerColor;
|
|
842
|
+
return {
|
|
843
|
+
value: h.value,
|
|
844
|
+
icon: h.icon,
|
|
845
|
+
label: showLabels ? h.label : void 0,
|
|
846
|
+
accessibilityLabel: h.label,
|
|
847
|
+
checkedColor,
|
|
848
|
+
uncheckedColor,
|
|
849
|
+
style: [showLabels ? void 0 : { minWidth: COMPACT_MIN_WIDTH }, containerColor ? { backgroundColor: containerColor } : void 0]
|
|
850
|
+
};
|
|
851
|
+
})
|
|
852
|
+
}
|
|
853
|
+
);
|
|
877
854
|
|
|
878
855
|
// src/components/IconButton.tsx
|
|
879
856
|
import { IconButton as PaperIconButton, useTheme as useTheme7 } from "react-native-paper";
|
|
880
|
-
import { jsx as
|
|
857
|
+
import { jsx as jsx12 } from "react/jsx-runtime";
|
|
881
858
|
function IconButton({ variant, iconColor, containerColor, ...props }) {
|
|
882
859
|
const defaults = usePaperDefaults();
|
|
883
860
|
const { colors } = useTheme7();
|
|
884
861
|
const variantContainerColor = variant === "primary" ? colors.primaryContainer : variant === "secondary" ? colors.secondaryContainer : variant === "tertiary" ? colors.tertiaryContainer : variant === "surface" ? colors.surface : void 0;
|
|
885
862
|
const variantIconColor = variant === "primary" ? colors.onPrimaryContainer : variant === "secondary" ? colors.onSecondaryContainer : variant === "tertiary" ? colors.onTertiaryContainer : variant === "surface" ? colors.onSurface : void 0;
|
|
886
|
-
return /* @__PURE__ */
|
|
863
|
+
return /* @__PURE__ */ jsx12(PaperIconButton, { ...defaults.IconButton, containerColor: containerColor ?? variantContainerColor, iconColor: iconColor ?? variantIconColor, ...props });
|
|
887
864
|
}
|
|
888
865
|
|
|
866
|
+
// src/components/Menu.tsx
|
|
867
|
+
import { StyleSheet as StyleSheet5 } from "react-native";
|
|
868
|
+
import { Menu as PaperMenu } from "react-native-paper";
|
|
869
|
+
import { jsx as jsx13 } from "react/jsx-runtime";
|
|
870
|
+
var MenuComponent = ({ blur: blurProp, children, ...props }) => {
|
|
871
|
+
const blur = useBlur(blurProp);
|
|
872
|
+
return /* @__PURE__ */ jsx13(PaperMenu, { ...props, contentStyle: [styles4.content, props.contentStyle], children: /* @__PURE__ */ jsx13(BlurView, { blur, children }) });
|
|
873
|
+
};
|
|
874
|
+
var Menu = Object.assign(MenuComponent, {
|
|
875
|
+
Item: PaperMenu.Item
|
|
876
|
+
});
|
|
877
|
+
var styles4 = StyleSheet5.create({
|
|
878
|
+
content: { backgroundColor: "transparent", paddingVertical: 0 }
|
|
879
|
+
});
|
|
880
|
+
|
|
889
881
|
// src/components/PalettePicker.tsx
|
|
890
|
-
import { useState as
|
|
882
|
+
import { useState as useState6 } from "react";
|
|
891
883
|
import { StyleSheet as StyleSheet6, TouchableOpacity as TouchableOpacity2, View as View5 } from "react-native";
|
|
892
884
|
import { Icon as Icon2, TouchableRipple as TouchableRipple2, useTheme as useTheme8 } from "react-native-paper";
|
|
893
885
|
import { Fragment as Fragment3, jsx as jsx14, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
@@ -905,35 +897,39 @@ var buildWedges = (weights) => {
|
|
|
905
897
|
];
|
|
906
898
|
};
|
|
907
899
|
var Pie = ({ palette, weights = defaultWeights, size = SIZE2 }) => /* @__PURE__ */ jsx14(View5, { style: [styles5.pie, { borderRadius: size / 2, height: size, width: size }], children: buildWedges(weights).map(({ key, rotate, share }) => /* @__PURE__ */ jsx14(View5, { style: [styles5.rotator, { transform: [{ rotate: `${rotate}deg` }] }], children: /* @__PURE__ */ jsx14(View5, { style: styles5.halfMask, children: /* @__PURE__ */ jsx14(View5, { style: [styles5.innerRotator, { transform: [{ rotate: `${share - 180}deg` }] }], children: /* @__PURE__ */ jsx14(View5, { style: [styles5.wedgeFill, { backgroundColor: palette[key] }] }) }) }) }, key)) });
|
|
908
|
-
var PalettePicker = ({ value, onChange, harmony = "split-complementary", weights, colors = defaultColors, blur: blurProp }) => {
|
|
900
|
+
var PalettePicker = ({ value, onChange, harmony = "split-complementary", onHarmonyChange, weights, colors = defaultColors, blur: blurProp }) => {
|
|
909
901
|
const blur = useBlur(blurProp);
|
|
910
902
|
const { colors: themeColors } = useTheme8();
|
|
911
|
-
const [open, setOpen] =
|
|
903
|
+
const [open, setOpen] = useState6(false);
|
|
912
904
|
const { onLayout, size } = useSwatchSize(SIZE2);
|
|
913
905
|
return /* @__PURE__ */ jsxs6(Fragment3, { children: [
|
|
914
906
|
/* @__PURE__ */ jsx14(TouchableRipple2, { onPress: () => setOpen(true), style: styles5.trigger, borderless: true, children: /* @__PURE__ */ jsxs6(View5, { style: styles5.triggerContent, children: [
|
|
915
907
|
/* @__PURE__ */ jsx14(Pie, { palette: getTriadicPalette(value, harmony), weights }),
|
|
916
908
|
/* @__PURE__ */ jsx14(View5, { pointerEvents: "none", style: [styles5.triggerIcon, { borderRadius: SIZE2 / 2 }, getSwatchRing(value, false, themeColors.outlineVariant)], children: /* @__PURE__ */ jsx14(Icon2, { source: "palette", size: 20, color: getContrastColor(value) }) })
|
|
917
909
|
] }) }),
|
|
918
|
-
/* @__PURE__ */ jsx14(Dialog, { blur, visible: open, onDismiss: () => setOpen(false), children: /* @__PURE__ */
|
|
919
|
-
|
|
920
|
-
{
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
910
|
+
/* @__PURE__ */ jsx14(Dialog, { blur, visible: open, onDismiss: () => setOpen(false), children: /* @__PURE__ */ jsxs6(Dialog.Content, { children: [
|
|
911
|
+
onHarmonyChange && /* @__PURE__ */ jsx14(View5, { style: styles5.harmonyRow, children: /* @__PURE__ */ jsx14(HarmonyPicker, { value: harmony, onChange: onHarmonyChange, showLabels: false }) }),
|
|
912
|
+
/* @__PURE__ */ jsx14(View5, { style: styles5.swatches, onLayout, children: colors.map(({ value: hex }) => /* @__PURE__ */ jsxs6(
|
|
913
|
+
TouchableOpacity2,
|
|
914
|
+
{
|
|
915
|
+
onPress: () => {
|
|
916
|
+
onChange(hex);
|
|
917
|
+
setOpen(false);
|
|
918
|
+
},
|
|
919
|
+
style: [styles5.swatch, { height: size, width: size }],
|
|
920
|
+
children: [
|
|
921
|
+
/* @__PURE__ */ jsx14(Pie, { palette: getTriadicPalette(hex, harmony), weights, size }),
|
|
922
|
+
/* @__PURE__ */ jsx14(View5, { pointerEvents: "none", style: [styles5.ring, { borderRadius: size / 2 }, getSwatchRing(hex, value === hex, themeColors.outlineVariant)], children: value === hex && /* @__PURE__ */ jsx14(Icon2, { source: "check", size: 20, color: getContrastColor(hex) }) })
|
|
923
|
+
]
|
|
924
924
|
},
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
/* @__PURE__ */ jsx14(View5, { pointerEvents: "none", style: [styles5.ring, { borderRadius: size / 2 }, getSwatchRing(hex, value === hex, themeColors.outlineVariant)], children: value === hex && /* @__PURE__ */ jsx14(Icon2, { source: "check", size: 20, color: getContrastColor(hex) }) })
|
|
929
|
-
]
|
|
930
|
-
},
|
|
931
|
-
hex
|
|
932
|
-
)) }) }) })
|
|
925
|
+
hex
|
|
926
|
+
)) })
|
|
927
|
+
] }) })
|
|
933
928
|
] });
|
|
934
929
|
};
|
|
935
930
|
var styles5 = StyleSheet6.create({
|
|
936
931
|
halfMask: { height: "100%", left: "50%", overflow: "hidden", position: "absolute", top: 0, width: "50%" },
|
|
932
|
+
harmonyRow: { marginBottom: 4 },
|
|
937
933
|
innerRotator: { height: "100%", left: "-100%", overflow: "hidden", position: "absolute", top: 0, width: "200%" },
|
|
938
934
|
pie: { overflow: "hidden" },
|
|
939
935
|
rotator: { height: "100%", left: 0, overflow: "hidden", position: "absolute", top: 0, width: "100%" },
|
package/package.json
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Button } from 'react-native-paper'
|
|
1
|
+
import { SegmentedButtons } from 'react-native-paper'
|
|
3
2
|
|
|
4
|
-
import { useBlur } from '../BlurContext'
|
|
5
3
|
import type { ColorHarmony } from '../utils/getTriadicPalette'
|
|
6
|
-
import { Menu } from './Menu'
|
|
7
4
|
|
|
8
5
|
const HARMONIES: { label: string; value: ColorHarmony; icon: string }[] = [
|
|
9
6
|
{ label: 'Triadic', value: 'triadic', icon: 'triangle-outline' },
|
|
@@ -14,39 +11,52 @@ const HARMONIES: { label: string; value: ColorHarmony; icon: string }[] = [
|
|
|
14
11
|
{ label: 'Dbl Split', value: 'double-split', icon: 'arrow-expand-horizontal' }
|
|
15
12
|
]
|
|
16
13
|
|
|
14
|
+
// Paper's own SegmentedButtonItem hardcodes `minWidth: 76` per segment (sized for a labeled text
|
|
15
|
+
// button), which 6 of never fit inside PalettePicker's own dialog card (capped at 400, and
|
|
16
|
+
// narrower still once its padding and safe-area margins are subtracted) — they just overflow past
|
|
17
|
+
// its `overflow: hidden` edge instead of shrinking. A fixed smaller guess (say 44) only moves the
|
|
18
|
+
// breakpoint rather than removing it — 6x44 still exceeds the card's content width on an ordinary
|
|
19
|
+
// ~360-375px phone. Zeroing the floor instead lets each segment's own `flex: 1` divide whatever
|
|
20
|
+
// width IS available with nothing left to enforce a minimum, so 6 of them mathematically can never
|
|
21
|
+
// exceed their container at any screen size — same "shrink to fit, never overflow" contract
|
|
22
|
+
// useSwatchGrid.ts's own SWATCH_GRID_COLUMNS already uses for the swatches below this row. Left
|
|
23
|
+
// untouched in labeled mode, where the wider floor is still doing its job of fitting the text.
|
|
24
|
+
const COMPACT_MIN_WIDTH = 0
|
|
25
|
+
|
|
17
26
|
type Props = {
|
|
18
27
|
value: ColorHarmony
|
|
19
28
|
onChange: (harmony: ColorHarmony) => void
|
|
20
|
-
|
|
29
|
+
// Hides text labels, leaving just the icon per segment — mirrors AppearancePicker's own
|
|
30
|
+
// showLabels, useful here since there are 6 segments to fit instead of 3.
|
|
31
|
+
// accessibilityLabel is always set regardless, so screen readers still announce the full
|
|
32
|
+
// harmony name either way.
|
|
33
|
+
showLabels?: boolean
|
|
34
|
+
// Per-segment fill, e.g. theme.colors.secondary/onSecondary for the checked segment and
|
|
35
|
+
// secondaryContainer/onSecondaryContainer for the rest — matches Paper's own checked/
|
|
36
|
+
// unchecked + container color vocabulary. Omit to fall back to Paper's default segmented-
|
|
37
|
+
// button styling.
|
|
38
|
+
checkedColor?: string
|
|
39
|
+
uncheckedColor?: string
|
|
40
|
+
checkedContainerColor?: string
|
|
41
|
+
uncheckedContainerColor?: string
|
|
21
42
|
}
|
|
22
43
|
|
|
23
|
-
export const HarmonyPicker = ({ value, onChange,
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
44
|
+
export const HarmonyPicker = ({ value, onChange, showLabels = true, checkedColor, uncheckedColor, checkedContainerColor, uncheckedContainerColor }: Props) => (
|
|
45
|
+
<SegmentedButtons
|
|
46
|
+
value={value}
|
|
47
|
+
onValueChange={(v) => onChange(v as ColorHarmony)}
|
|
48
|
+
buttons={HARMONIES.map((h) => {
|
|
49
|
+
const checked = h.value === value
|
|
50
|
+
const containerColor = checked ? checkedContainerColor : uncheckedContainerColor
|
|
51
|
+
return {
|
|
52
|
+
value: h.value,
|
|
53
|
+
icon: h.icon,
|
|
54
|
+
label: showLabels ? h.label : undefined,
|
|
55
|
+
accessibilityLabel: h.label,
|
|
56
|
+
checkedColor,
|
|
57
|
+
uncheckedColor,
|
|
58
|
+
style: [showLabels ? undefined : { minWidth: COMPACT_MIN_WIDTH }, containerColor ? { backgroundColor: containerColor } : undefined]
|
|
37
59
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
key={h.value}
|
|
42
|
-
leadingIcon={h.value === value ? 'check' : h.icon}
|
|
43
|
-
title={h.label}
|
|
44
|
-
onPress={() => {
|
|
45
|
-
onChange(h.value)
|
|
46
|
-
setOpen(false)
|
|
47
|
-
}}
|
|
48
|
-
/>
|
|
49
|
-
))}
|
|
50
|
-
</Menu>
|
|
51
|
-
)
|
|
52
|
-
}
|
|
60
|
+
})}
|
|
61
|
+
/>
|
|
62
|
+
)
|
|
@@ -10,6 +10,7 @@ import { getTriadicPalette } from '../utils/getTriadicPalette'
|
|
|
10
10
|
import type { SeedColor } from './ColorPicker'
|
|
11
11
|
import { defaultColors } from './ColorPicker'
|
|
12
12
|
import { Dialog } from './Dialog'
|
|
13
|
+
import { HarmonyPicker } from './HarmonyPicker'
|
|
13
14
|
|
|
14
15
|
const SIZE = 48
|
|
15
16
|
|
|
@@ -55,12 +56,18 @@ type Props = {
|
|
|
55
56
|
value: string
|
|
56
57
|
onChange: (color: string) => void
|
|
57
58
|
harmony?: ColorHarmony
|
|
59
|
+
// Opt-in: passing this turns on a compact, icon-only HarmonyPicker row inside the dialog, above
|
|
60
|
+
// the swatch grid, so every swatch's own Pie preview reshapes live as harmony changes. Omit it
|
|
61
|
+
// and this renders exactly as a plain PalettePicker always has — same trigger, same dialog,
|
|
62
|
+
// harmony fixed at whatever the `harmony` prop says. Gated on the callback itself rather than a
|
|
63
|
+
// separate boolean so there's no way to pass one without the other.
|
|
64
|
+
onHarmonyChange?: (harmony: ColorHarmony) => void
|
|
58
65
|
weights?: PaletteWeights
|
|
59
66
|
colors?: SeedColor[]
|
|
60
67
|
blur?: boolean
|
|
61
68
|
}
|
|
62
69
|
|
|
63
|
-
export const PalettePicker = ({ value, onChange, harmony = 'split-complementary', weights, colors = defaultColors, blur: blurProp }: Props) => {
|
|
70
|
+
export const PalettePicker = ({ value, onChange, harmony = 'split-complementary', onHarmonyChange, weights, colors = defaultColors, blur: blurProp }: Props) => {
|
|
64
71
|
const blur = useBlur(blurProp)
|
|
65
72
|
const { colors: themeColors } = useTheme()
|
|
66
73
|
const [open, setOpen] = useState(false)
|
|
@@ -79,6 +86,11 @@ export const PalettePicker = ({ value, onChange, harmony = 'split-complementary'
|
|
|
79
86
|
|
|
80
87
|
<Dialog blur={blur} visible={open} onDismiss={() => setOpen(false)}>
|
|
81
88
|
<Dialog.Content>
|
|
89
|
+
{onHarmonyChange && (
|
|
90
|
+
<View style={styles.harmonyRow}>
|
|
91
|
+
<HarmonyPicker value={harmony} onChange={onHarmonyChange} showLabels={false} />
|
|
92
|
+
</View>
|
|
93
|
+
)}
|
|
82
94
|
<View style={styles.swatches} onLayout={onLayout}>
|
|
83
95
|
{colors.map(({ value: hex }) => (
|
|
84
96
|
<TouchableOpacity
|
|
@@ -104,6 +116,7 @@ export const PalettePicker = ({ value, onChange, harmony = 'split-complementary'
|
|
|
104
116
|
|
|
105
117
|
const styles = StyleSheet.create({
|
|
106
118
|
halfMask: { height: '100%', left: '50%', overflow: 'hidden', position: 'absolute', top: 0, width: '50%' },
|
|
119
|
+
harmonyRow: { marginBottom: 4 },
|
|
107
120
|
innerRotator: { height: '100%', left: '-100%', overflow: 'hidden', position: 'absolute', top: 0, width: '200%' },
|
|
108
121
|
pie: { overflow: 'hidden' },
|
|
109
122
|
rotator: { height: '100%', left: 0, overflow: 'hidden', position: 'absolute', top: 0, width: '100%' },
|