@cdx-ui/primitives 0.1.7 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/commonjs/color/Color.js +69 -0
- package/lib/commonjs/color/Color.js.map +1 -0
- package/lib/commonjs/color/index.js +13 -0
- package/lib/commonjs/color/index.js.map +1 -0
- package/lib/commonjs/color/types.js +2 -0
- package/lib/commonjs/color/types.js.map +1 -0
- package/lib/commonjs/color-area/ColorAreaAccessibility.js +100 -0
- package/lib/commonjs/color-area/ColorAreaAccessibility.js.map +1 -0
- package/lib/commonjs/color-area/ColorAreaAccessibility.web.js +58 -0
- package/lib/commonjs/color-area/ColorAreaAccessibility.web.js.map +1 -0
- package/lib/commonjs/color-area/ColorAreaRoot.js +240 -0
- package/lib/commonjs/color-area/ColorAreaRoot.js.map +1 -0
- package/lib/commonjs/color-area/ColorAreaThumb.js +71 -0
- package/lib/commonjs/color-area/ColorAreaThumb.js.map +1 -0
- package/lib/commonjs/color-area/context.js +11 -0
- package/lib/commonjs/color-area/context.js.map +1 -0
- package/lib/commonjs/color-area/index.js +27 -0
- package/lib/commonjs/color-area/index.js.map +1 -0
- package/lib/commonjs/color-area/types.js +6 -0
- package/lib/commonjs/color-area/types.js.map +1 -0
- package/lib/commonjs/color-area/utils.js +51 -0
- package/lib/commonjs/color-area/utils.js.map +1 -0
- package/lib/commonjs/color-field/ColorFieldInput.js +67 -0
- package/lib/commonjs/color-field/ColorFieldInput.js.map +1 -0
- package/lib/commonjs/color-field/ColorFieldRoot.js +104 -0
- package/lib/commonjs/color-field/ColorFieldRoot.js.map +1 -0
- package/lib/commonjs/color-field/context.js +11 -0
- package/lib/commonjs/color-field/context.js.map +1 -0
- package/lib/commonjs/color-field/index.js +27 -0
- package/lib/commonjs/color-field/index.js.map +1 -0
- package/lib/commonjs/color-field/types.js +6 -0
- package/lib/commonjs/color-field/types.js.map +1 -0
- package/lib/commonjs/color-field/utils.js +60 -0
- package/lib/commonjs/color-field/utils.js.map +1 -0
- package/lib/commonjs/color-picker/ColorPickerRoot.js +69 -0
- package/lib/commonjs/color-picker/ColorPickerRoot.js.map +1 -0
- package/lib/commonjs/color-picker/ColorPickerTrigger.js +98 -0
- package/lib/commonjs/color-picker/ColorPickerTrigger.js.map +1 -0
- package/lib/commonjs/color-picker/context.js +32 -0
- package/lib/commonjs/color-picker/context.js.map +1 -0
- package/lib/commonjs/color-picker/index.js +51 -0
- package/lib/commonjs/color-picker/index.js.map +1 -0
- package/lib/commonjs/color-picker/types.js +6 -0
- package/lib/commonjs/color-picker/types.js.map +1 -0
- package/lib/commonjs/color-slider/ColorSliderRail.js +119 -0
- package/lib/commonjs/color-slider/ColorSliderRail.js.map +1 -0
- package/lib/commonjs/color-slider/ColorSliderRoot.js +153 -0
- package/lib/commonjs/color-slider/ColorSliderRoot.js.map +1 -0
- package/lib/commonjs/color-slider/ColorSliderThumb.js +265 -0
- package/lib/commonjs/color-slider/ColorSliderThumb.js.map +1 -0
- package/lib/commonjs/color-slider/context.js +11 -0
- package/lib/commonjs/color-slider/context.js.map +1 -0
- package/lib/commonjs/color-slider/index.js +34 -0
- package/lib/commonjs/color-slider/index.js.map +1 -0
- package/lib/commonjs/color-slider/types.js +6 -0
- package/lib/commonjs/color-slider/types.js.map +1 -0
- package/lib/commonjs/color-slider/utils.js +75 -0
- package/lib/commonjs/color-slider/utils.js.map +1 -0
- package/lib/commonjs/color-swatch-picker/ColorSwatchPickerItem.js +46 -0
- package/lib/commonjs/color-swatch-picker/ColorSwatchPickerItem.js.map +1 -0
- package/lib/commonjs/color-swatch-picker/ColorSwatchPickerRoot.js +71 -0
- package/lib/commonjs/color-swatch-picker/ColorSwatchPickerRoot.js.map +1 -0
- package/lib/commonjs/color-swatch-picker/context.js +22 -0
- package/lib/commonjs/color-swatch-picker/context.js.map +1 -0
- package/lib/commonjs/color-swatch-picker/index.js +33 -0
- package/lib/commonjs/color-swatch-picker/index.js.map +1 -0
- package/lib/commonjs/color-swatch-picker/types.js +6 -0
- package/lib/commonjs/color-swatch-picker/types.js.map +1 -0
- package/lib/commonjs/index.js +80 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/react-native-web.d.js +2 -0
- package/lib/commonjs/react-native-web.d.js.map +1 -0
- package/lib/commonjs/slider/useSliderPointer.js +2 -0
- package/lib/commonjs/slider/useSliderPointer.js.map +1 -1
- package/lib/commonjs/utils/createPortal.web.js +10 -1
- package/lib/commonjs/utils/createPortal.web.js.map +1 -1
- package/lib/commonjs/utils/textAncestor.js +25 -0
- package/lib/commonjs/utils/textAncestor.js.map +1 -0
- package/lib/commonjs/utils/textAncestor.web.js +27 -0
- package/lib/commonjs/utils/textAncestor.web.js.map +1 -0
- package/lib/module/color/Color.js +65 -0
- package/lib/module/color/Color.js.map +1 -0
- package/lib/module/color/index.js +4 -0
- package/lib/module/color/index.js.map +1 -0
- package/lib/module/color/types.js +2 -0
- package/lib/module/color/types.js.map +1 -0
- package/lib/module/color-area/ColorAreaAccessibility.js +96 -0
- package/lib/module/color-area/ColorAreaAccessibility.js.map +1 -0
- package/lib/module/color-area/ColorAreaAccessibility.web.js +54 -0
- package/lib/module/color-area/ColorAreaAccessibility.web.js.map +1 -0
- package/lib/module/color-area/ColorAreaRoot.js +236 -0
- package/lib/module/color-area/ColorAreaRoot.js.map +1 -0
- package/lib/module/color-area/ColorAreaThumb.js +67 -0
- package/lib/module/color-area/ColorAreaThumb.js.map +1 -0
- package/lib/module/color-area/context.js +5 -0
- package/lib/module/color-area/context.js.map +1 -0
- package/lib/module/color-area/index.js +6 -0
- package/lib/module/color-area/index.js.map +1 -0
- package/lib/module/color-area/types.js +4 -0
- package/lib/module/color-area/types.js.map +1 -0
- package/lib/module/color-area/utils.js +45 -0
- package/lib/module/color-area/utils.js.map +1 -0
- package/lib/module/color-field/ColorFieldInput.js +63 -0
- package/lib/module/color-field/ColorFieldInput.js.map +1 -0
- package/lib/module/color-field/ColorFieldRoot.js +100 -0
- package/lib/module/color-field/ColorFieldRoot.js.map +1 -0
- package/lib/module/color-field/context.js +5 -0
- package/lib/module/color-field/context.js.map +1 -0
- package/lib/module/color-field/index.js +6 -0
- package/lib/module/color-field/index.js.map +1 -0
- package/lib/module/color-field/types.js +4 -0
- package/lib/module/color-field/types.js.map +1 -0
- package/lib/module/color-field/utils.js +55 -0
- package/lib/module/color-field/utils.js.map +1 -0
- package/lib/module/color-picker/ColorPickerRoot.js +65 -0
- package/lib/module/color-picker/ColorPickerRoot.js.map +1 -0
- package/lib/module/color-picker/ColorPickerTrigger.js +94 -0
- package/lib/module/color-picker/ColorPickerTrigger.js.map +1 -0
- package/lib/module/color-picker/context.js +25 -0
- package/lib/module/color-picker/context.js.map +1 -0
- package/lib/module/color-picker/index.js +6 -0
- package/lib/module/color-picker/index.js.map +1 -0
- package/lib/module/color-picker/types.js +4 -0
- package/lib/module/color-picker/types.js.map +1 -0
- package/lib/module/color-slider/ColorSliderRail.js +115 -0
- package/lib/module/color-slider/ColorSliderRail.js.map +1 -0
- package/lib/module/color-slider/ColorSliderRoot.js +149 -0
- package/lib/module/color-slider/ColorSliderRoot.js.map +1 -0
- package/lib/module/color-slider/ColorSliderThumb.js +261 -0
- package/lib/module/color-slider/ColorSliderThumb.js.map +1 -0
- package/lib/module/color-slider/context.js +5 -0
- package/lib/module/color-slider/context.js.map +1 -0
- package/lib/module/color-slider/index.js +7 -0
- package/lib/module/color-slider/index.js.map +1 -0
- package/lib/module/color-slider/types.js +4 -0
- package/lib/module/color-slider/types.js.map +1 -0
- package/lib/module/color-slider/utils.js +66 -0
- package/lib/module/color-slider/utils.js.map +1 -0
- package/lib/module/color-swatch-picker/ColorSwatchPickerItem.js +42 -0
- package/lib/module/color-swatch-picker/ColorSwatchPickerItem.js.map +1 -0
- package/lib/module/color-swatch-picker/ColorSwatchPickerRoot.js +67 -0
- package/lib/module/color-swatch-picker/ColorSwatchPickerRoot.js.map +1 -0
- package/lib/module/color-swatch-picker/context.js +15 -0
- package/lib/module/color-swatch-picker/context.js.map +1 -0
- package/lib/module/color-swatch-picker/index.js +6 -0
- package/lib/module/color-swatch-picker/index.js.map +1 -0
- package/lib/module/color-swatch-picker/types.js +4 -0
- package/lib/module/color-swatch-picker/types.js.map +1 -0
- package/lib/module/index.js +7 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/react-native-web.d.js +2 -0
- package/lib/module/react-native-web.d.js.map +1 -0
- package/lib/module/slider/useSliderPointer.js +2 -0
- package/lib/module/slider/useSliderPointer.js.map +1 -1
- package/lib/module/utils/createPortal.web.js +9 -1
- package/lib/module/utils/createPortal.web.js.map +1 -1
- package/lib/module/utils/textAncestor.js +20 -0
- package/lib/module/utils/textAncestor.js.map +1 -0
- package/lib/module/utils/textAncestor.web.js +22 -0
- package/lib/module/utils/textAncestor.web.js.map +1 -0
- package/lib/typescript/color/Color.d.ts +3 -0
- package/lib/typescript/color/Color.d.ts.map +1 -0
- package/lib/typescript/color/index.d.ts +3 -0
- package/lib/typescript/color/index.d.ts.map +1 -0
- package/lib/typescript/color/types.d.ts +13 -0
- package/lib/typescript/color/types.d.ts.map +1 -0
- package/lib/typescript/color-area/ColorAreaAccessibility.d.ts +8 -0
- package/lib/typescript/color-area/ColorAreaAccessibility.d.ts.map +1 -0
- package/lib/typescript/color-area/ColorAreaAccessibility.web.d.ts +8 -0
- package/lib/typescript/color-area/ColorAreaAccessibility.web.d.ts.map +1 -0
- package/lib/typescript/color-area/ColorAreaRoot.d.ts +7 -0
- package/lib/typescript/color-area/ColorAreaRoot.d.ts.map +1 -0
- package/lib/typescript/color-area/ColorAreaThumb.d.ts +6 -0
- package/lib/typescript/color-area/ColorAreaThumb.d.ts.map +1 -0
- package/lib/typescript/color-area/context.d.ts +6 -0
- package/lib/typescript/color-area/context.d.ts.map +1 -0
- package/lib/typescript/color-area/index.d.ts +5 -0
- package/lib/typescript/color-area/index.d.ts.map +1 -0
- package/lib/typescript/color-area/types.d.ts +65 -0
- package/lib/typescript/color-area/types.d.ts.map +1 -0
- package/lib/typescript/color-area/utils.d.ts +5 -0
- package/lib/typescript/color-area/utils.d.ts.map +1 -0
- package/lib/typescript/color-field/ColorFieldInput.d.ts +5 -0
- package/lib/typescript/color-field/ColorFieldInput.d.ts.map +1 -0
- package/lib/typescript/color-field/ColorFieldRoot.d.ts +7 -0
- package/lib/typescript/color-field/ColorFieldRoot.d.ts.map +1 -0
- package/lib/typescript/color-field/context.d.ts +6 -0
- package/lib/typescript/color-field/context.d.ts.map +1 -0
- package/lib/typescript/color-field/index.d.ts +7 -0
- package/lib/typescript/color-field/index.d.ts.map +1 -0
- package/lib/typescript/color-field/types.d.ts +39 -0
- package/lib/typescript/color-field/types.d.ts.map +1 -0
- package/lib/typescript/color-field/utils.d.ts +5 -0
- package/lib/typescript/color-field/utils.d.ts.map +1 -0
- package/lib/typescript/color-picker/ColorPickerRoot.d.ts +7 -0
- package/lib/typescript/color-picker/ColorPickerRoot.d.ts.map +1 -0
- package/lib/typescript/color-picker/ColorPickerTrigger.d.ts +4 -0
- package/lib/typescript/color-picker/ColorPickerTrigger.d.ts.map +1 -0
- package/lib/typescript/color-picker/context.d.ts +19 -0
- package/lib/typescript/color-picker/context.d.ts.map +1 -0
- package/lib/typescript/color-picker/index.d.ts +5 -0
- package/lib/typescript/color-picker/index.d.ts.map +1 -0
- package/lib/typescript/color-picker/types.d.ts +24 -0
- package/lib/typescript/color-picker/types.d.ts.map +1 -0
- package/lib/typescript/color-slider/ColorSliderRail.d.ts +6 -0
- package/lib/typescript/color-slider/ColorSliderRail.d.ts.map +1 -0
- package/lib/typescript/color-slider/ColorSliderRoot.d.ts +7 -0
- package/lib/typescript/color-slider/ColorSliderRoot.d.ts.map +1 -0
- package/lib/typescript/color-slider/ColorSliderThumb.d.ts +6 -0
- package/lib/typescript/color-slider/ColorSliderThumb.d.ts.map +1 -0
- package/lib/typescript/color-slider/context.d.ts +6 -0
- package/lib/typescript/color-slider/context.d.ts.map +1 -0
- package/lib/typescript/color-slider/index.d.ts +6 -0
- package/lib/typescript/color-slider/index.d.ts.map +1 -0
- package/lib/typescript/color-slider/types.d.ts +82 -0
- package/lib/typescript/color-slider/types.d.ts.map +1 -0
- package/lib/typescript/color-slider/utils.d.ts +12 -0
- package/lib/typescript/color-slider/utils.d.ts.map +1 -0
- package/lib/typescript/color-swatch-picker/ColorSwatchPickerItem.d.ts +6 -0
- package/lib/typescript/color-swatch-picker/ColorSwatchPickerItem.d.ts.map +1 -0
- package/lib/typescript/color-swatch-picker/ColorSwatchPickerRoot.d.ts +7 -0
- package/lib/typescript/color-swatch-picker/ColorSwatchPickerRoot.d.ts.map +1 -0
- package/lib/typescript/color-swatch-picker/context.d.ts +8 -0
- package/lib/typescript/color-swatch-picker/context.d.ts.map +1 -0
- package/lib/typescript/color-swatch-picker/index.d.ts +5 -0
- package/lib/typescript/color-swatch-picker/index.d.ts.map +1 -0
- package/lib/typescript/color-swatch-picker/types.d.ts +26 -0
- package/lib/typescript/color-swatch-picker/types.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +7 -0
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/slider/useSliderPointer.d.ts.map +1 -1
- package/lib/typescript/utils/createPortal.web.d.ts +6 -2
- package/lib/typescript/utils/createPortal.web.d.ts.map +1 -1
- package/lib/typescript/utils/textAncestor.d.ts +13 -0
- package/lib/typescript/utils/textAncestor.d.ts.map +1 -0
- package/lib/typescript/utils/textAncestor.web.d.ts +15 -0
- package/lib/typescript/utils/textAncestor.web.d.ts.map +1 -0
- package/package.json +4 -3
- package/src/color/Color.ts +95 -0
- package/src/color/index.ts +2 -0
- package/src/color/types.ts +19 -0
- package/src/color-area/ColorAreaAccessibility.tsx +94 -0
- package/src/color-area/ColorAreaAccessibility.web.tsx +52 -0
- package/src/color-area/ColorAreaRoot.tsx +326 -0
- package/src/color-area/ColorAreaThumb.tsx +56 -0
- package/src/color-area/context.tsx +5 -0
- package/src/color-area/index.ts +10 -0
- package/src/color-area/types.ts +69 -0
- package/src/color-area/utils.ts +51 -0
- package/src/color-field/ColorFieldInput.tsx +66 -0
- package/src/color-field/ColorFieldRoot.tsx +132 -0
- package/src/color-field/context.tsx +5 -0
- package/src/color-field/index.ts +11 -0
- package/src/color-field/types.ts +46 -0
- package/src/color-field/utils.ts +75 -0
- package/src/color-picker/ColorPickerRoot.tsx +91 -0
- package/src/color-picker/ColorPickerTrigger.tsx +96 -0
- package/src/color-picker/context.tsx +47 -0
- package/src/color-picker/index.ts +12 -0
- package/src/color-picker/types.ts +25 -0
- package/src/color-slider/ColorSliderRail.tsx +130 -0
- package/src/color-slider/ColorSliderRoot.tsx +222 -0
- package/src/color-slider/ColorSliderThumb.tsx +294 -0
- package/src/color-slider/context.tsx +5 -0
- package/src/color-slider/index.ts +16 -0
- package/src/color-slider/types.ts +94 -0
- package/src/color-slider/utils.ts +79 -0
- package/src/color-swatch-picker/ColorSwatchPickerItem.tsx +54 -0
- package/src/color-swatch-picker/ColorSwatchPickerRoot.tsx +94 -0
- package/src/color-swatch-picker/context.tsx +22 -0
- package/src/color-swatch-picker/index.ts +4 -0
- package/src/color-swatch-picker/types.ts +29 -0
- package/src/index.ts +7 -0
- package/src/react-native-web.d.ts +6 -0
- package/src/slider/useSliderPointer.ts +3 -0
- package/src/utils/createPortal.web.ts +9 -3
- package/src/utils/textAncestor.ts +19 -0
- package/src/utils/textAncestor.web.ts +19 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { ViewProps } from 'react-native';
|
|
3
|
+
import type { Color } from '../color';
|
|
4
|
+
|
|
5
|
+
export interface IColorAreaProps extends Omit<ViewProps, 'children'> {
|
|
6
|
+
/** Current controlled color. */
|
|
7
|
+
readonly value?: Color;
|
|
8
|
+
/** Initial color for uncontrolled usage. */
|
|
9
|
+
readonly defaultValue?: Color;
|
|
10
|
+
/** Called while either color channel changes. */
|
|
11
|
+
readonly onValueChange?: (value: Color) => void;
|
|
12
|
+
/** Called when a pointer, keyboard, or screen reader interaction completes. */
|
|
13
|
+
readonly onValueChangeComplete?: (value: Color) => void;
|
|
14
|
+
/** Color space represented by the area. @default `'hsb'` */
|
|
15
|
+
readonly colorSpace?: 'hsb';
|
|
16
|
+
/** Horizontal channel. @default `'saturation'` */
|
|
17
|
+
readonly xChannel?: 'saturation';
|
|
18
|
+
/** Vertical channel. @default `'brightness'` */
|
|
19
|
+
readonly yChannel?: 'brightness';
|
|
20
|
+
/** Arrow-key and screen-reader increment. @default `1` */
|
|
21
|
+
readonly step?: number;
|
|
22
|
+
/** Shift-arrow and page-key increment. @default `10` */
|
|
23
|
+
readonly pageStep?: number;
|
|
24
|
+
/** Overrides the platform layout direction for horizontal interaction. */
|
|
25
|
+
readonly isRTL?: boolean;
|
|
26
|
+
/** Disables pointer, keyboard, and screen reader interaction. @default `false` */
|
|
27
|
+
readonly isDisabled?: boolean;
|
|
28
|
+
readonly children?: ReactNode;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface IColorAreaThumbProps extends ViewProps {
|
|
32
|
+
readonly asChild?: boolean;
|
|
33
|
+
readonly children?: ReactNode;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface ColorAreaLayout {
|
|
37
|
+
readonly width: number;
|
|
38
|
+
readonly height: number;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface ColorAreaContextValue {
|
|
42
|
+
readonly value: Color;
|
|
43
|
+
readonly xValue: number;
|
|
44
|
+
readonly yValue: number;
|
|
45
|
+
readonly xPercent: number;
|
|
46
|
+
readonly yPercent: number;
|
|
47
|
+
readonly layout: ColorAreaLayout;
|
|
48
|
+
readonly thumbSize: ColorAreaLayout;
|
|
49
|
+
readonly isRTL: boolean;
|
|
50
|
+
readonly isDisabled: boolean;
|
|
51
|
+
readonly step: number;
|
|
52
|
+
readonly pageStep: number;
|
|
53
|
+
readonly setLayout: (layout: ColorAreaLayout) => void;
|
|
54
|
+
readonly setThumbSize: (size: ColorAreaLayout) => void;
|
|
55
|
+
readonly setChannelsFromPoint: (x: number, y: number, complete?: boolean) => void;
|
|
56
|
+
readonly setXValue: (value: number, complete?: boolean) => void;
|
|
57
|
+
readonly setYValue: (value: number, complete?: boolean) => void;
|
|
58
|
+
readonly handleKeyDown: (event: ColorAreaKeyboardEvent) => void;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface ColorAreaKeyboardEvent {
|
|
62
|
+
readonly nativeEvent?: {
|
|
63
|
+
readonly key?: string;
|
|
64
|
+
readonly shiftKey?: boolean;
|
|
65
|
+
};
|
|
66
|
+
readonly key?: string;
|
|
67
|
+
readonly shiftKey?: boolean;
|
|
68
|
+
preventDefault?: () => void;
|
|
69
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { Color } from '../color';
|
|
2
|
+
|
|
3
|
+
export function clampColorChannel(value: number, min = 0, max = 100): number {
|
|
4
|
+
if (!Number.isFinite(value)) {
|
|
5
|
+
return min;
|
|
6
|
+
}
|
|
7
|
+
return Math.min(max, Math.max(min, value));
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function snapColorChannel(value: number, step: number, min = 0, max = 100): number {
|
|
11
|
+
const clamped = clampColorChannel(value, min, max);
|
|
12
|
+
const snapped = step > 0 ? min + Math.round((clamped - min) / step) * step : clamped;
|
|
13
|
+
return clampColorChannel(snapped, min, max);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function describeColor(color: Color): string {
|
|
17
|
+
const hue = clampColorChannel(color.getChannel('hsb', 'hue'), 0, 360);
|
|
18
|
+
const saturation = clampColorChannel(color.getChannel('hsb', 'saturation'));
|
|
19
|
+
const brightness = clampColorChannel(color.getChannel('hsb', 'brightness'));
|
|
20
|
+
|
|
21
|
+
if (brightness <= 10) {
|
|
22
|
+
return 'black';
|
|
23
|
+
}
|
|
24
|
+
if (saturation <= 10) {
|
|
25
|
+
if (brightness >= 90) {
|
|
26
|
+
return 'white';
|
|
27
|
+
}
|
|
28
|
+
return brightness < 45 ? 'dark gray' : brightness > 75 ? 'light gray' : 'gray';
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const names = ['red', 'orange', 'yellow', 'green', 'cyan', 'blue', 'purple', 'magenta'];
|
|
32
|
+
const index = Math.round((((hue % 360) + 360) % 360) / 45) % names.length;
|
|
33
|
+
const tone = brightness < 40 ? 'dark ' : brightness > 80 ? 'light ' : '';
|
|
34
|
+
const intensity = saturation > 75 ? 'vibrant ' : saturation < 40 ? 'muted ' : '';
|
|
35
|
+
|
|
36
|
+
return `${tone}${intensity}${names[index]}`;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function colorAreaValueText(color: Color, changedChannel?: 'saturation' | 'brightness') {
|
|
40
|
+
const saturation = Math.round(clampColorChannel(color.getChannel('hsb', 'saturation')));
|
|
41
|
+
const brightness = Math.round(clampColorChannel(color.getChannel('hsb', 'brightness')));
|
|
42
|
+
const colorName = describeColor(color);
|
|
43
|
+
|
|
44
|
+
if (changedChannel === 'saturation') {
|
|
45
|
+
return `Saturation ${String(saturation)}%, ${colorName}`;
|
|
46
|
+
}
|
|
47
|
+
if (changedChannel === 'brightness') {
|
|
48
|
+
return `Brightness ${String(brightness)}%, ${colorName}`;
|
|
49
|
+
}
|
|
50
|
+
return `Saturation ${String(saturation)}%, Brightness ${String(brightness)}%, ${colorName}`;
|
|
51
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { forwardRef, useEffect, useState } from 'react';
|
|
2
|
+
import { TextInput } from 'react-native';
|
|
3
|
+
import { InputField } from '../input';
|
|
4
|
+
import { useColorFieldContext } from './context';
|
|
5
|
+
import type { IColorFieldInputProps } from './types';
|
|
6
|
+
|
|
7
|
+
export type ColorFieldInputProps = IColorFieldInputProps;
|
|
8
|
+
|
|
9
|
+
export const ColorFieldInput = forwardRef<TextInput, ColorFieldInputProps>(
|
|
10
|
+
(
|
|
11
|
+
{
|
|
12
|
+
onChangeText,
|
|
13
|
+
onFocus,
|
|
14
|
+
onBlur,
|
|
15
|
+
editable,
|
|
16
|
+
keyboardType,
|
|
17
|
+
autoCapitalize = 'none',
|
|
18
|
+
autoCorrect = false,
|
|
19
|
+
...props
|
|
20
|
+
},
|
|
21
|
+
ref,
|
|
22
|
+
) => {
|
|
23
|
+
const { value, colorSpace, disabled, readOnly, invalid, required, commitText, formatValue } =
|
|
24
|
+
useColorFieldContext();
|
|
25
|
+
const [text, setText] = useState(formatValue);
|
|
26
|
+
const [isEditing, setIsEditing] = useState(false);
|
|
27
|
+
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
if (!isEditing) {
|
|
30
|
+
setText(formatValue());
|
|
31
|
+
}
|
|
32
|
+
}, [formatValue, isEditing, value]);
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<InputField
|
|
36
|
+
ref={ref}
|
|
37
|
+
{...props}
|
|
38
|
+
value={text}
|
|
39
|
+
editable={editable ?? !(disabled || readOnly)}
|
|
40
|
+
disabled={disabled}
|
|
41
|
+
isDisabled={disabled}
|
|
42
|
+
isReadOnly={readOnly}
|
|
43
|
+
isInvalid={invalid}
|
|
44
|
+
isRequired={required}
|
|
45
|
+
keyboardType={keyboardType ?? (colorSpace ? 'decimal-pad' : 'default')}
|
|
46
|
+
autoCapitalize={autoCapitalize}
|
|
47
|
+
autoCorrect={autoCorrect}
|
|
48
|
+
onChangeText={(nextText) => {
|
|
49
|
+
onChangeText?.(nextText);
|
|
50
|
+
setText(nextText);
|
|
51
|
+
commitText(nextText);
|
|
52
|
+
}}
|
|
53
|
+
onFocus={(event) => {
|
|
54
|
+
setIsEditing(true);
|
|
55
|
+
onFocus?.(event);
|
|
56
|
+
}}
|
|
57
|
+
onBlur={(event) => {
|
|
58
|
+
setIsEditing(false);
|
|
59
|
+
onBlur?.(event);
|
|
60
|
+
}}
|
|
61
|
+
/>
|
|
62
|
+
);
|
|
63
|
+
},
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
ColorFieldInput.displayName = 'ColorFieldInput';
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { forwardRef, useCallback, useMemo, useRef } from 'react';
|
|
2
|
+
import { View } from 'react-native';
|
|
3
|
+
import { useControllableState, useFormControl } from '@cdx-ui/utils';
|
|
4
|
+
import { useOptionalColorPickerContext } from '../color-picker';
|
|
5
|
+
import type { Color } from '../color';
|
|
6
|
+
import { Slot } from '../slot';
|
|
7
|
+
import { dataAttributes, mergeDataAttributes } from '../utils/dataAttributes';
|
|
8
|
+
import { ColorFieldProvider } from './context';
|
|
9
|
+
import type { ColorFieldConfiguration, ColorFieldContextValue, IColorFieldProps } from './types';
|
|
10
|
+
import { formatColorFieldValue, parseColorFieldValue } from './utils';
|
|
11
|
+
|
|
12
|
+
export type ColorFieldRootProps = IColorFieldProps & {
|
|
13
|
+
readonly asChild?: boolean;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const ColorFieldRoot = forwardRef<View, ColorFieldRootProps>(
|
|
17
|
+
(
|
|
18
|
+
{
|
|
19
|
+
asChild,
|
|
20
|
+
value: valueProp,
|
|
21
|
+
defaultValue,
|
|
22
|
+
onValueChange,
|
|
23
|
+
colorSpace,
|
|
24
|
+
channel,
|
|
25
|
+
isDisabled,
|
|
26
|
+
isReadOnly,
|
|
27
|
+
isInvalid,
|
|
28
|
+
isRequired,
|
|
29
|
+
children,
|
|
30
|
+
...props
|
|
31
|
+
},
|
|
32
|
+
ref,
|
|
33
|
+
) => {
|
|
34
|
+
const picker = useOptionalColorPickerContext();
|
|
35
|
+
const usesPickerContext =
|
|
36
|
+
picker !== null && valueProp === undefined && defaultValue === undefined;
|
|
37
|
+
const [localValue, setLocalValue] = useControllableState<Color>({
|
|
38
|
+
prop: valueProp,
|
|
39
|
+
defaultProp: defaultValue,
|
|
40
|
+
onChange: onValueChange,
|
|
41
|
+
});
|
|
42
|
+
const value = usesPickerContext ? picker.value : localValue;
|
|
43
|
+
const valueRef = useRef(value);
|
|
44
|
+
valueRef.current = value;
|
|
45
|
+
|
|
46
|
+
const formControl = useFormControl({
|
|
47
|
+
isDisabled,
|
|
48
|
+
isReadOnly,
|
|
49
|
+
isInvalid,
|
|
50
|
+
isRequired,
|
|
51
|
+
});
|
|
52
|
+
const disabled = Boolean(formControl.disabled);
|
|
53
|
+
const readOnly = Boolean(formControl.readOnly);
|
|
54
|
+
const invalid = Boolean(formControl['aria-invalid']);
|
|
55
|
+
const required = Boolean(formControl.required);
|
|
56
|
+
const configuration = useMemo<ColorFieldConfiguration>(
|
|
57
|
+
() =>
|
|
58
|
+
channel
|
|
59
|
+
? ({ colorSpace, channel } as ColorFieldConfiguration)
|
|
60
|
+
: { colorSpace: undefined, channel: undefined },
|
|
61
|
+
[channel, colorSpace],
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
const commitText = useCallback(
|
|
65
|
+
(text: string) => {
|
|
66
|
+
if (disabled || readOnly) {
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const nextValue = parseColorFieldValue(text, valueRef.current, configuration);
|
|
71
|
+
if (!nextValue) {
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
valueRef.current = nextValue;
|
|
76
|
+
if (usesPickerContext) {
|
|
77
|
+
onValueChange?.(nextValue);
|
|
78
|
+
picker.onValueChange(nextValue);
|
|
79
|
+
} else {
|
|
80
|
+
setLocalValue(nextValue);
|
|
81
|
+
}
|
|
82
|
+
return nextValue;
|
|
83
|
+
},
|
|
84
|
+
[configuration, disabled, onValueChange, picker, readOnly, setLocalValue, usesPickerContext],
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
const formatValue = useCallback(
|
|
88
|
+
() => formatColorFieldValue(valueRef.current, configuration),
|
|
89
|
+
[configuration],
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
const contextValue = useMemo<ColorFieldContextValue>(
|
|
93
|
+
() => ({
|
|
94
|
+
...configuration,
|
|
95
|
+
value,
|
|
96
|
+
disabled,
|
|
97
|
+
readOnly,
|
|
98
|
+
invalid,
|
|
99
|
+
required,
|
|
100
|
+
commitText,
|
|
101
|
+
formatValue,
|
|
102
|
+
}),
|
|
103
|
+
[commitText, configuration, disabled, formatValue, invalid, readOnly, required, value],
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
const Comp = asChild ? Slot : View;
|
|
107
|
+
|
|
108
|
+
return (
|
|
109
|
+
<ColorFieldProvider value={contextValue}>
|
|
110
|
+
<Comp
|
|
111
|
+
ref={ref}
|
|
112
|
+
{...props}
|
|
113
|
+
{...mergeDataAttributes(
|
|
114
|
+
props,
|
|
115
|
+
dataAttributes({
|
|
116
|
+
disabled,
|
|
117
|
+
invalid,
|
|
118
|
+
readonly: readOnly,
|
|
119
|
+
required,
|
|
120
|
+
colorSpace: configuration.colorSpace,
|
|
121
|
+
channel: configuration.channel,
|
|
122
|
+
}),
|
|
123
|
+
)}
|
|
124
|
+
>
|
|
125
|
+
{children}
|
|
126
|
+
</Comp>
|
|
127
|
+
</ColorFieldProvider>
|
|
128
|
+
);
|
|
129
|
+
},
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
ColorFieldRoot.displayName = 'ColorFieldRoot';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { ColorFieldRoot } from './ColorFieldRoot';
|
|
2
|
+
export type { ColorFieldRootProps } from './ColorFieldRoot';
|
|
3
|
+
export { ColorFieldInput } from './ColorFieldInput';
|
|
4
|
+
export type { ColorFieldInputProps } from './ColorFieldInput';
|
|
5
|
+
export { useColorFieldContext } from './context';
|
|
6
|
+
export type {
|
|
7
|
+
ColorFieldCommonProps,
|
|
8
|
+
ColorFieldConfiguration,
|
|
9
|
+
IColorFieldInputProps,
|
|
10
|
+
IColorFieldProps,
|
|
11
|
+
} from './types';
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { ViewProps } from 'react-native';
|
|
3
|
+
import type { IInputProps } from '../input';
|
|
4
|
+
import type { Color } from '../color';
|
|
5
|
+
|
|
6
|
+
export type ColorFieldConfiguration =
|
|
7
|
+
| {
|
|
8
|
+
readonly colorSpace?: undefined;
|
|
9
|
+
readonly channel?: undefined;
|
|
10
|
+
}
|
|
11
|
+
| {
|
|
12
|
+
readonly colorSpace: 'rgb';
|
|
13
|
+
readonly channel: 'red' | 'green' | 'blue';
|
|
14
|
+
}
|
|
15
|
+
| {
|
|
16
|
+
readonly colorSpace: 'hsl';
|
|
17
|
+
readonly channel: 'hue' | 'saturation' | 'lightness';
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export interface ColorFieldCommonProps extends Omit<ViewProps, 'children'> {
|
|
21
|
+
/** Current controlled color. */
|
|
22
|
+
readonly value?: Color;
|
|
23
|
+
/** Initial color for uncontrolled usage. */
|
|
24
|
+
readonly defaultValue?: Color;
|
|
25
|
+
/** Called when the input contains a valid color or channel value. */
|
|
26
|
+
readonly onValueChange?: (value: Color) => void;
|
|
27
|
+
readonly isDisabled?: boolean;
|
|
28
|
+
readonly isReadOnly?: boolean;
|
|
29
|
+
readonly isInvalid?: boolean;
|
|
30
|
+
readonly isRequired?: boolean;
|
|
31
|
+
readonly children?: ReactNode;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export type IColorFieldProps = ColorFieldCommonProps & ColorFieldConfiguration;
|
|
35
|
+
|
|
36
|
+
export type IColorFieldInputProps = Omit<IInputProps, 'value' | 'defaultValue'>;
|
|
37
|
+
|
|
38
|
+
export type ColorFieldContextValue = ColorFieldConfiguration & {
|
|
39
|
+
readonly value: Color | undefined;
|
|
40
|
+
readonly disabled: boolean;
|
|
41
|
+
readonly readOnly: boolean;
|
|
42
|
+
readonly invalid: boolean;
|
|
43
|
+
readonly required: boolean;
|
|
44
|
+
readonly commitText: (text: string) => Color | undefined;
|
|
45
|
+
readonly formatValue: () => string;
|
|
46
|
+
};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { parseColor, type Color } from '../color';
|
|
2
|
+
import type { ColorFieldConfiguration } from './types';
|
|
3
|
+
|
|
4
|
+
const CHANNEL_VALUE_PATTERN = /^([+-]?(?:\d+(?:\.\d+)?|\.\d+))\s*([°%])?$/u;
|
|
5
|
+
|
|
6
|
+
function formatNumber(value: number): string {
|
|
7
|
+
return Number.isInteger(value) ? String(value) : value.toFixed(1);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function formatColorFieldValue(
|
|
11
|
+
value: Color | undefined,
|
|
12
|
+
configuration: ColorFieldConfiguration,
|
|
13
|
+
): string {
|
|
14
|
+
if (!value) {
|
|
15
|
+
return '';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (!configuration.channel) {
|
|
19
|
+
return value.toString('hex');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const channelValue = value.getChannel(configuration.colorSpace, configuration.channel);
|
|
23
|
+
const formattedValue = formatNumber(channelValue);
|
|
24
|
+
|
|
25
|
+
if (configuration.channel === 'hue') {
|
|
26
|
+
return `${formattedValue}°`;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (configuration.colorSpace === 'hsl') {
|
|
30
|
+
return `${formattedValue}%`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return formattedValue;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function parseColorFieldValue(
|
|
37
|
+
text: string,
|
|
38
|
+
currentValue: Color | undefined,
|
|
39
|
+
configuration: ColorFieldConfiguration,
|
|
40
|
+
): Color | undefined {
|
|
41
|
+
const normalizedText = text.trim();
|
|
42
|
+
|
|
43
|
+
if (!configuration.channel) {
|
|
44
|
+
return parseColor(normalizedText);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (!currentValue) {
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const match = CHANNEL_VALUE_PATTERN.exec(normalizedText);
|
|
52
|
+
if (!match) {
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const value = Number(match[1]);
|
|
57
|
+
const unit = match[2];
|
|
58
|
+
if (!Number.isFinite(value)) {
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (configuration.colorSpace === 'rgb') {
|
|
63
|
+
if (unit || value < 0 || value > 255) {
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
} else if (configuration.channel === 'hue') {
|
|
67
|
+
if (unit === '%' || value < 0 || value > 360) {
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
} else if (unit === '°' || value < 0 || value > 100) {
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return currentValue.withChannel(configuration.colorSpace, configuration.channel, value);
|
|
75
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { forwardRef, useCallback, useMemo, useRef, type ComponentRef } from 'react';
|
|
2
|
+
import { View } from 'react-native';
|
|
3
|
+
import { mergeRefs, useControllableState } from '@cdx-ui/utils';
|
|
4
|
+
import type { Color } from '../color';
|
|
5
|
+
import { Slot } from '../slot';
|
|
6
|
+
import { dataAttributes, mergeDataAttributes } from '../utils/dataAttributes';
|
|
7
|
+
import { ColorPickerProvider, ColorPickerRootProvider } from './context';
|
|
8
|
+
import type { IColorPickerProps } from './types';
|
|
9
|
+
|
|
10
|
+
export interface ColorPickerRootProps extends IColorPickerProps {
|
|
11
|
+
readonly asChild?: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const ColorPickerRoot = forwardRef<ComponentRef<typeof View>, ColorPickerRootProps>(
|
|
15
|
+
(
|
|
16
|
+
{
|
|
17
|
+
asChild,
|
|
18
|
+
value: valueProp,
|
|
19
|
+
defaultValue,
|
|
20
|
+
onValueChange,
|
|
21
|
+
isOpen: openProp,
|
|
22
|
+
defaultOpen = false,
|
|
23
|
+
onOpenChange,
|
|
24
|
+
children,
|
|
25
|
+
...props
|
|
26
|
+
},
|
|
27
|
+
ref,
|
|
28
|
+
) => {
|
|
29
|
+
const [value, setValueState] = useControllableState<Color>({
|
|
30
|
+
prop: valueProp,
|
|
31
|
+
defaultProp: defaultValue,
|
|
32
|
+
onChange: onValueChange,
|
|
33
|
+
});
|
|
34
|
+
const [open, setOpenState] = useControllableState<boolean>({
|
|
35
|
+
prop: openProp,
|
|
36
|
+
defaultProp: defaultOpen,
|
|
37
|
+
onChange: onOpenChange,
|
|
38
|
+
});
|
|
39
|
+
const anchorRef = useRef<ComponentRef<typeof View>>(null);
|
|
40
|
+
const triggerRef = useRef<ComponentRef<typeof View>>(null);
|
|
41
|
+
|
|
42
|
+
const setValue = useCallback(
|
|
43
|
+
(nextValue: Color) => {
|
|
44
|
+
setValueState(nextValue);
|
|
45
|
+
},
|
|
46
|
+
[setValueState],
|
|
47
|
+
);
|
|
48
|
+
const setOpen = useCallback(
|
|
49
|
+
(nextOpen: boolean) => {
|
|
50
|
+
setOpenState(nextOpen);
|
|
51
|
+
},
|
|
52
|
+
[setOpenState],
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
const contextValue = useMemo(
|
|
56
|
+
() => ({
|
|
57
|
+
value,
|
|
58
|
+
onValueChange: setValue,
|
|
59
|
+
open: open ?? false,
|
|
60
|
+
setOpen,
|
|
61
|
+
anchorRef,
|
|
62
|
+
triggerRef,
|
|
63
|
+
}),
|
|
64
|
+
[open, setOpen, setValue, value],
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
const Comp = asChild ? Slot : View;
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
<ColorPickerRootProvider value={contextValue}>
|
|
71
|
+
<ColorPickerProvider value={contextValue}>
|
|
72
|
+
<Comp
|
|
73
|
+
ref={mergeRefs(ref, anchorRef)}
|
|
74
|
+
{...props}
|
|
75
|
+
{...mergeDataAttributes(
|
|
76
|
+
props,
|
|
77
|
+
dataAttributes({
|
|
78
|
+
open: contextValue.open,
|
|
79
|
+
state: contextValue.open ? 'open' : 'closed',
|
|
80
|
+
}),
|
|
81
|
+
)}
|
|
82
|
+
>
|
|
83
|
+
{children}
|
|
84
|
+
</Comp>
|
|
85
|
+
</ColorPickerProvider>
|
|
86
|
+
</ColorPickerRootProvider>
|
|
87
|
+
);
|
|
88
|
+
},
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
ColorPickerRoot.displayName = 'ColorPickerRoot';
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { forwardRef, type ComponentRef } from 'react';
|
|
2
|
+
import { Platform, Pressable, type PressableProps } from 'react-native';
|
|
3
|
+
import { composeEventHandlers, mergeRefs } from '@cdx-ui/utils';
|
|
4
|
+
import { useFocus, useFocusRing } from '@react-native-aria/focus';
|
|
5
|
+
import { useHover, usePress } from '@react-native-aria/interactions';
|
|
6
|
+
import { Slot } from '../slot';
|
|
7
|
+
import { dataAttributes, mergeDataAttributes } from '../utils/dataAttributes';
|
|
8
|
+
import { useColorPickerRootContext } from './context';
|
|
9
|
+
import type { IColorPickerTriggerProps } from './types';
|
|
10
|
+
|
|
11
|
+
export type ColorPickerTriggerProps = IColorPickerTriggerProps;
|
|
12
|
+
|
|
13
|
+
export const ColorPickerTrigger = forwardRef<
|
|
14
|
+
ComponentRef<typeof Pressable>,
|
|
15
|
+
ColorPickerTriggerProps
|
|
16
|
+
>(
|
|
17
|
+
(
|
|
18
|
+
{
|
|
19
|
+
asChild,
|
|
20
|
+
children,
|
|
21
|
+
isDisabled,
|
|
22
|
+
isHovered: isHoveredProp,
|
|
23
|
+
isActive: isActiveProp,
|
|
24
|
+
isFocused: isFocusedProp,
|
|
25
|
+
isFocusVisible: isFocusVisibleProp,
|
|
26
|
+
accessibilityLabel = 'Open color picker',
|
|
27
|
+
onPress,
|
|
28
|
+
onPressIn,
|
|
29
|
+
onPressOut,
|
|
30
|
+
onHoverIn,
|
|
31
|
+
onHoverOut,
|
|
32
|
+
onFocus,
|
|
33
|
+
onBlur,
|
|
34
|
+
...props
|
|
35
|
+
},
|
|
36
|
+
ref,
|
|
37
|
+
) => {
|
|
38
|
+
const { open, setOpen, triggerRef } = useColorPickerRootContext();
|
|
39
|
+
const disabled = Boolean(isDisabled || props.disabled);
|
|
40
|
+
const { isFocused, focusProps } = useFocus();
|
|
41
|
+
const { isFocusVisible, focusProps: focusRingProps } = useFocusRing() as {
|
|
42
|
+
isFocusVisible: boolean;
|
|
43
|
+
focusProps: typeof focusProps;
|
|
44
|
+
};
|
|
45
|
+
const { isPressed, pressProps } = usePress({ isDisabled: disabled }) as {
|
|
46
|
+
isPressed: boolean;
|
|
47
|
+
pressProps: Pick<PressableProps, 'onPressIn' | 'onPressOut'>;
|
|
48
|
+
};
|
|
49
|
+
const { isHovered, hoverProps } = useHover({ isDisabled: disabled });
|
|
50
|
+
const Comp = asChild ? Slot : Pressable;
|
|
51
|
+
const platformAccessibilityProps =
|
|
52
|
+
Platform.OS === 'web'
|
|
53
|
+
? {
|
|
54
|
+
role: 'button' as const,
|
|
55
|
+
'aria-haspopup': 'dialog' as const,
|
|
56
|
+
'aria-expanded': open,
|
|
57
|
+
'aria-disabled': disabled || undefined,
|
|
58
|
+
}
|
|
59
|
+
: {
|
|
60
|
+
accessibilityRole: 'button' as const,
|
|
61
|
+
accessibilityState: { disabled, expanded: open },
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<Comp
|
|
66
|
+
ref={mergeRefs(ref, triggerRef)}
|
|
67
|
+
disabled={disabled}
|
|
68
|
+
accessibilityLabel={accessibilityLabel}
|
|
69
|
+
{...props}
|
|
70
|
+
{...platformAccessibilityProps}
|
|
71
|
+
{...mergeDataAttributes(
|
|
72
|
+
props,
|
|
73
|
+
dataAttributes({
|
|
74
|
+
state: open ? 'open' : 'closed',
|
|
75
|
+
disabled,
|
|
76
|
+
hover: isHoveredProp || isHovered,
|
|
77
|
+
active: isActiveProp || isPressed,
|
|
78
|
+
focus: isFocusedProp || isFocused,
|
|
79
|
+
focusVisible: isFocusVisibleProp || isFocusVisible,
|
|
80
|
+
}),
|
|
81
|
+
)}
|
|
82
|
+
onPress={composeEventHandlers(onPress, () => setOpen(!open))}
|
|
83
|
+
onPressIn={composeEventHandlers(onPressIn, pressProps.onPressIn)}
|
|
84
|
+
onPressOut={composeEventHandlers(onPressOut, pressProps.onPressOut)}
|
|
85
|
+
onHoverIn={composeEventHandlers(onHoverIn, hoverProps.onHoverIn)}
|
|
86
|
+
onHoverOut={composeEventHandlers(onHoverOut, hoverProps.onHoverOut)}
|
|
87
|
+
onFocus={composeEventHandlers(onFocus, focusProps.onFocus, focusRingProps.onFocus)}
|
|
88
|
+
onBlur={composeEventHandlers(onBlur, focusProps.onBlur, focusRingProps.onBlur)}
|
|
89
|
+
>
|
|
90
|
+
{children}
|
|
91
|
+
</Comp>
|
|
92
|
+
);
|
|
93
|
+
},
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
ColorPickerTrigger.displayName = 'ColorPickerTrigger';
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { createContext, useContext, type RefObject } from 'react';
|
|
2
|
+
import type { View } from 'react-native';
|
|
3
|
+
import type { Color } from '../color';
|
|
4
|
+
|
|
5
|
+
export interface ColorPickerContextValue {
|
|
6
|
+
value: Color | undefined;
|
|
7
|
+
onValueChange: (value: Color) => void;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const ColorPickerContext = createContext<ColorPickerContextValue | null>(null);
|
|
11
|
+
|
|
12
|
+
export const ColorPickerProvider = ColorPickerContext.Provider;
|
|
13
|
+
|
|
14
|
+
export interface ColorPickerRootContextValue extends ColorPickerContextValue {
|
|
15
|
+
open: boolean;
|
|
16
|
+
setOpen: (open: boolean) => void;
|
|
17
|
+
anchorRef: RefObject<View | null>;
|
|
18
|
+
triggerRef: RefObject<View | null>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const ColorPickerRootContext = createContext<ColorPickerRootContextValue | null>(null);
|
|
22
|
+
|
|
23
|
+
export const ColorPickerRootProvider = ColorPickerRootContext.Provider;
|
|
24
|
+
|
|
25
|
+
export function useColorPickerContext(): ColorPickerContextValue {
|
|
26
|
+
const context = useContext(ColorPickerContext);
|
|
27
|
+
|
|
28
|
+
if (!context) {
|
|
29
|
+
throw new Error('Color picker compound components must be used within ColorPicker');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return context;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function useOptionalColorPickerContext(): ColorPickerContextValue | null {
|
|
36
|
+
return useContext(ColorPickerContext);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function useColorPickerRootContext(): ColorPickerRootContextValue {
|
|
40
|
+
const context = useContext(ColorPickerRootContext);
|
|
41
|
+
|
|
42
|
+
if (!context) {
|
|
43
|
+
throw new Error('ColorPicker compound components must be used within ColorPickerRoot');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return context;
|
|
47
|
+
}
|