import { shallowRef } from 'vue'; import type { ComputedRef, ShallowRef } from 'vue'; import { defaultWindow } from '@/types'; import type { ConfigurableWindow } from '@/types'; import { useSupported } from '@/composables/utilities/useSupported'; export interface EyeDropperOpenOptions { /** * An `AbortSignal` that can be used to cancel the operation. * * @see https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal */ signal?: AbortSignal; } export interface EyeDropperResult { /** * The selected color, in sRGB hexadecimal format (e.g. `#a1b2c3`). */ sRGBHex: string; } export interface EyeDropper { open: (options?: EyeDropperOpenOptions) => Promise; [Symbol.toStringTag]: 'EyeDropper'; } export type EyeDropperConstructor = new () => EyeDropper; export interface UseEyeDropperOptions extends ConfigurableWindow { /** * Initial `sRGBHex` value before any color has been picked. * * @default '' */ initialValue?: string; } export interface UseEyeDropperReturn { /** * Whether the [EyeDropper API](https://developer.mozilla.org/en-US/docs/Web/API/EyeDropper) is supported */ isSupported: ComputedRef; /** * The most recently picked color, in sRGB hexadecimal format */ sRGBHex: ShallowRef; /** * Open the eyedropper and let the user pick a color. Resolves with the * result, or `undefined` when the API is unsupported. */ open: (openOptions?: EyeDropperOpenOptions) => Promise; } /** * @name useEyeDropper * @category Browser * @description Reactive wrapper around the [EyeDropper API](https://developer.mozilla.org/en-US/docs/Web/API/EyeDropper) for picking colors from the screen. * * @param {UseEyeDropperOptions} [options={}] Options * @returns {UseEyeDropperReturn} `isSupported`, `sRGBHex`, and `open()` * * @example * const { isSupported, sRGBHex, open } = useEyeDropper(); * if (isSupported.value) * await open(); * * @since 0.0.14 */ export function useEyeDropper(options: UseEyeDropperOptions = {}): UseEyeDropperReturn { const { window = defaultWindow, initialValue = '', } = options; const isSupported = useSupported(() => !!window && 'EyeDropper' in window); const sRGBHex = shallowRef(initialValue); async function open(openOptions?: EyeDropperOpenOptions): Promise { if (!isSupported.value || !window) return; const EyeDropperCtor = (window as unknown as { EyeDropper: EyeDropperConstructor }).EyeDropper; const eyeDropper = new EyeDropperCtor(); const result = await eyeDropper.open(openOptions); sRGBHex.value = result.sRGBHex; return result; } return { isSupported, sRGBHex, open, }; }