feat: implement color playground with interactive picker and info display
Add complete color manipulation interface: **Color Components:** - ColorPicker - Interactive hex color picker with text input - Uses react-colorful for visual selection - Manual input support for any color format - Real-time updates - ColorDisplay - Large color preview swatch - Configurable sizes (sm, md, lg, xl) - Optional border styling - Accessible with ARIA labels - ColorInfo - Comprehensive color information display - Shows all formats: Hex, RGB, HSL, Lab, OkLab - Copy to clipboard functionality for each format - Displays brightness, luminance, light/dark type - Shows named color matches **API Integration:** - React Query hooks for all Pastel API endpoints - useColorInfo - Get color information - useConvertFormat - Format conversion - useLighten, useDarken, useSaturate, etc. - Manipulations - useGenerateRandom, useGenerateDistinct, useGenerateGradient - useNamedColors - Cached named colors list - Automatic error handling and loading states **Playground Page (/playground):** - Two-column layout: picker + info - Live color preview with large swatch - Real-time API integration with loading states - Error handling with user-friendly messages - Quick action buttons (ready for implementation) - Responsive grid layout **Features:** - Toast notifications for clipboard copy - Loading spinners during API calls - Error display with helpful messages - Accessible keyboard navigation - Smooth transitions and animations Build successful with playground page rendering! Ready for color manipulation actions. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
206
lib/api/queries.ts
Normal file
206
lib/api/queries.ts
Normal file
@@ -0,0 +1,206 @@
|
||||
'use client';
|
||||
|
||||
import { useQuery, useMutation, UseQueryOptions } from '@tanstack/react-query';
|
||||
import { pastelAPI } from './client';
|
||||
import type {
|
||||
ColorInfoRequest,
|
||||
ColorInfoData,
|
||||
ConvertFormatRequest,
|
||||
ConvertFormatData,
|
||||
ColorManipulationRequest,
|
||||
ColorManipulationData,
|
||||
ColorMixRequest,
|
||||
ColorMixData,
|
||||
RandomColorsRequest,
|
||||
RandomColorsData,
|
||||
DistinctColorsRequest,
|
||||
DistinctColorsData,
|
||||
GradientRequest,
|
||||
GradientData,
|
||||
NamedColorsData,
|
||||
HealthData,
|
||||
} from './types';
|
||||
|
||||
// Color Information
|
||||
export const useColorInfo = (
|
||||
request: ColorInfoRequest,
|
||||
options?: Omit<UseQueryOptions<ColorInfoData>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: ['colorInfo', request.colors],
|
||||
queryFn: async () => {
|
||||
const response = await pastelAPI.getColorInfo(request);
|
||||
if (!response.success) {
|
||||
throw new Error(response.error.message);
|
||||
}
|
||||
return response.data;
|
||||
},
|
||||
enabled: request.colors.length > 0 && request.colors.every((c) => c.length > 0),
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
// Format Conversion
|
||||
export const useConvertFormat = () => {
|
||||
return useMutation({
|
||||
mutationFn: async (request: ConvertFormatRequest) => {
|
||||
const response = await pastelAPI.convertFormat(request);
|
||||
if (!response.success) {
|
||||
throw new Error(response.error.message);
|
||||
}
|
||||
return response.data;
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// Color Manipulation
|
||||
export const useLighten = () => {
|
||||
return useMutation({
|
||||
mutationFn: async (request: ColorManipulationRequest) => {
|
||||
const response = await pastelAPI.lighten(request);
|
||||
if (!response.success) {
|
||||
throw new Error(response.error.message);
|
||||
}
|
||||
return response.data;
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useDarken = () => {
|
||||
return useMutation({
|
||||
mutationFn: async (request: ColorManipulationRequest) => {
|
||||
const response = await pastelAPI.darken(request);
|
||||
if (!response.success) {
|
||||
throw new Error(response.error.message);
|
||||
}
|
||||
return response.data;
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useSaturate = () => {
|
||||
return useMutation({
|
||||
mutationFn: async (request: ColorManipulationRequest) => {
|
||||
const response = await pastelAPI.saturate(request);
|
||||
if (!response.success) {
|
||||
throw new Error(response.error.message);
|
||||
}
|
||||
return response.data;
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useDesaturate = () => {
|
||||
return useMutation({
|
||||
mutationFn: async (request: ColorManipulationRequest) => {
|
||||
const response = await pastelAPI.desaturate(request);
|
||||
if (!response.success) {
|
||||
throw new Error(response.error.message);
|
||||
}
|
||||
return response.data;
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useRotate = () => {
|
||||
return useMutation({
|
||||
mutationFn: async (request: ColorManipulationRequest) => {
|
||||
const response = await pastelAPI.rotate(request);
|
||||
if (!response.success) {
|
||||
throw new Error(response.error.message);
|
||||
}
|
||||
return response.data;
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useComplement = () => {
|
||||
return useMutation({
|
||||
mutationFn: async (colors: string[]) => {
|
||||
const response = await pastelAPI.complement(colors);
|
||||
if (!response.success) {
|
||||
throw new Error(response.error.message);
|
||||
}
|
||||
return response.data;
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useMixColors = () => {
|
||||
return useMutation({
|
||||
mutationFn: async (request: ColorMixRequest) => {
|
||||
const response = await pastelAPI.mix(request);
|
||||
if (!response.success) {
|
||||
throw new Error(response.error.message);
|
||||
}
|
||||
return response.data;
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// Color Generation
|
||||
export const useGenerateRandom = () => {
|
||||
return useMutation({
|
||||
mutationFn: async (request: RandomColorsRequest) => {
|
||||
const response = await pastelAPI.generateRandom(request);
|
||||
if (!response.success) {
|
||||
throw new Error(response.error.message);
|
||||
}
|
||||
return response.data;
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useGenerateDistinct = () => {
|
||||
return useMutation({
|
||||
mutationFn: async (request: DistinctColorsRequest) => {
|
||||
const response = await pastelAPI.generateDistinct(request);
|
||||
if (!response.success) {
|
||||
throw new Error(response.error.message);
|
||||
}
|
||||
return response.data;
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useGenerateGradient = () => {
|
||||
return useMutation({
|
||||
mutationFn: async (request: GradientRequest) => {
|
||||
const response = await pastelAPI.generateGradient(request);
|
||||
if (!response.success) {
|
||||
throw new Error(response.error.message);
|
||||
}
|
||||
return response.data;
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// Named Colors
|
||||
export const useNamedColors = () => {
|
||||
return useQuery({
|
||||
queryKey: ['namedColors'],
|
||||
queryFn: async () => {
|
||||
const response = await pastelAPI.getNamedColors();
|
||||
if (!response.success) {
|
||||
throw new Error(response.error.message);
|
||||
}
|
||||
return response.data;
|
||||
},
|
||||
staleTime: Infinity, // Named colors never change
|
||||
});
|
||||
};
|
||||
|
||||
// Health Check
|
||||
export const useHealth = () => {
|
||||
return useQuery({
|
||||
queryKey: ['health'],
|
||||
queryFn: async () => {
|
||||
const response = await pastelAPI.getHealth();
|
||||
if (!response.success) {
|
||||
throw new Error(response.error.message);
|
||||
}
|
||||
return response.data;
|
||||
},
|
||||
refetchInterval: 60000, // Check every minute
|
||||
});
|
||||
};
|
||||
Reference in New Issue
Block a user