reanimated-color-picker
Version:
A Pure JavaScript Color Picker for React Native
243 lines (233 loc) • 7.44 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', {
value: true,
});
exports.default = void 0;
var _react = _interopRequireWildcard(require('react'));
var _reactNative = require('react-native');
var _reactNativeGestureHandler = require('react-native-gesture-handler');
var _reactNativeReanimated = require('react-native-reanimated');
var _index = _interopRequireDefault(require('./colorKit/index'));
var _AppContext = require('./AppContext');
var _utils = require('./utils');
function _interopRequireDefault(e) {
return e && e.__esModule ? e : { default: e };
}
function _interopRequireWildcard(e, t) {
if ('function' == typeof WeakMap)
var r = new WeakMap(),
n = new WeakMap();
return (_interopRequireWildcard = function (e, t) {
if (!t && e && e.__esModule) return e;
var o,
i,
f = { __proto__: null, default: e };
if (null === e || ('object' != typeof e && 'function' != typeof e)) return f;
if ((o = t ? n : r)) {
if (o.has(e)) return o.get(e);
o.set(e, f);
}
for (const t in e)
'default' !== t &&
{}.hasOwnProperty.call(e, t) &&
((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set)
? o(f, t, i)
: (f[t] = e[t]));
return f;
})(e, t);
}
// @ts-expect-error no global
if (_utils.isWeb && !global.setImmediate) {
// @ts-expect-error no global
global.setImmediate = setTimeout;
}
function ColorPickerWrapper(props, ref) {
const {
adaptSpectrum = false,
sliderThickness = 25,
thumbAnimationDuration = 200,
thumbSize = 35,
thumbShape = 'ring',
boundedThumb = false,
thumbScaleAnimationValue = 1.2,
thumbScaleAnimationDuration = 100,
thumbColor,
renderThumb,
thumbStyle,
thumbInnerStyle,
value = '#fff',
colorAnnouncementFormat = 'rgb',
enableColorAnnouncements = true,
onChange,
onChangeJS,
onComplete,
onCompleteJS,
style = {},
children = /*#__PURE__*/ _react.default.createElement(_reactNative.Text, null, 'NO CHILDREN'),
} = props;
const initialColor = (0, _react.useRef)(_index.default.HSV(value).object(false)).current;
const hueValue = (0, _reactNativeReanimated.useSharedValue)(initialColor.h);
const saturationValue = (0, _reactNativeReanimated.useSharedValue)(initialColor.s);
const brightnessValue = (0, _reactNativeReanimated.useSharedValue)(initialColor.v);
const alphaValue = (0, _reactNativeReanimated.useSharedValue)(initialColor.a);
/**
* Returns lazy getters for the provided color in multiple formats.
*
* @worklet
*/
const colorResult = inputColor => {
'worklet';
const color = inputColor ?? {
h: hueValue.value,
s: saturationValue.value,
v: brightnessValue.value,
a: alphaValue.value,
};
return {
get hex() {
return _index.default.runOnUI().HEX(color);
},
get rgb() {
return _index.default.runOnUI().RGB(color).string(false);
},
get rgba() {
return _index.default.runOnUI().RGB(color).string(true);
},
get hsl() {
return _index.default.runOnUI().HSL(color).string(false);
},
get hsla() {
return _index.default.runOnUI().HSL(color).string(true);
},
get hsv() {
return _index.default.runOnUI().HSV(color).string(false);
},
get hsva() {
return _index.default.runOnUI().HSV(color).string(true);
},
get hwb() {
return _index.default.runOnUI().HWB(color).string(false);
},
get hwba() {
return _index.default.runOnUI().HWB(color).string(true);
},
};
};
const announceColor = color => {
_reactNative.AccessibilityInfo.announceForAccessibility(color);
};
/** @worklet */
const onGestureEnd = color => {
'worklet';
const colorObject = colorResult(color);
if (enableColorAnnouncements && colorAnnouncementFormat in colorObject) {
(0, _reactNativeReanimated.runOnJS)(announceColor)(colorObject[colorAnnouncementFormat]);
}
if (onComplete) {
onComplete(colorObject);
}
if (onCompleteJS) {
(0, _reactNativeReanimated.runOnJS)(onCompleteJS)(colorObject);
}
};
/** @worklet */
const onGestureChange = color => {
'worklet';
if (!onChange && !onChangeJS) {
return;
}
const colorObject = colorResult(color);
if (onChange) {
onChange(colorObject);
}
if (onChangeJS) {
(0, _reactNativeReanimated.runOnJS)(onChangeJS)(colorObject);
}
};
const setColor = (color, duration = thumbAnimationDuration) => {
const { h, s, v, a } = _index.default.HSV(color).object(false);
hueValue.value = (0, _reactNativeReanimated.withTiming)(h, {
duration,
});
saturationValue.value = (0, _reactNativeReanimated.withTiming)(s, {
duration,
});
brightnessValue.value = (0, _reactNativeReanimated.withTiming)(v, {
duration,
});
alphaValue.value = (0, _reactNativeReanimated.withTiming)(a, {
duration,
});
};
// Prevent color shift caused by precision loss during format conversion.
// The color picker operates in HSV internally, so any incoming color is converted
// to HSV and back to the target format on every render. This round-trip can produce
// slightly different values than the original, causing sliders to drift. Before
// calling setColor, we convert the current color to the incoming format and compare
// channel values — if they match, the colors are perceptually identical and we skip
// the update.
(0, _react.useEffect)(() => {
const incomingColor = _index.default.parse(value);
if (!incomingColor) {
return setColor(value);
}
const HEX_FORMATS = new Set(['hex3', 'hex4', 'hex6', 'hex8', 'named']);
const incomingFormat = HEX_FORMATS.has(incomingColor.format) ? 'hex' : incomingColor.format;
const currentColor = _index.default.parse(colorResult()[incomingFormat] ?? '');
if (!currentColor) {
return setColor(value);
}
const isEqual =
Object.keys(incomingColor.value).length === Object.keys(currentColor.value).length &&
Object.entries(incomingColor.value).every(([k, v]) => currentColor.value[k] === v);
if (!isEqual) {
setColor(value);
}
}, [value]);
(0, _react.useImperativeHandle)(ref, () => ({
setColor,
}));
const ctxValue = {
hueValue,
saturationValue,
brightnessValue,
alphaValue,
adaptSpectrum,
sliderThickness,
thumbSize,
thumbShape,
boundedThumb,
thumbColor,
renderThumb,
thumbStyle,
thumbInnerStyle,
thumbScaleAnimationValue,
thumbScaleAnimationDuration,
value,
setColor,
colorResult,
onGestureEnd,
onGestureChange,
};
return /*#__PURE__*/ _react.default.createElement(
_reactNativeGestureHandler.GestureHandlerRootView,
{
style: [
{
direction: _utils.isWeb ? 'ltr' : undefined,
},
style,
],
},
/*#__PURE__*/ _react.default.createElement(
_AppContext.PickerContextProvider,
{
value: ctxValue,
},
children,
),
);
}
/** @see [ColorPicker](https://alabsi91.github.io/reanimated-color-picker/api/color-picker-wrapper/) */
const ColorPicker = /*#__PURE__*/ (0, _react.forwardRef)(ColorPickerWrapper);
var _default = (exports.default = ColorPicker);