diff --git a/packages/alto/src/_dev/App.vue b/packages/alto/src/_dev/App.vue index 2fd7a1b6..d4057ffc 100644 --- a/packages/alto/src/_dev/App.vue +++ b/packages/alto/src/_dev/App.vue @@ -2,56 +2,43 @@ import 'uno.css'; import '../assets/main.css'; import { ref } from 'vue'; -import { Status } from '~/components'; -import type { StatusObject } from '~/components/Status/types'; +import type { TagItemValue } from '../types'; +import { ColorInput, ColorPicker, Tag } from '~/components'; -const staticFruit = ref({ - color: '#fffad2', - label: 'Banana 🍌', - labelColor: '#555022', -}); +const colorv3 = ref('#A8B1FFFF'); +const color = ref('#A8B1FFFF'); -const fruits = [ - { - color: '#ffdecb', - label: 'Peach 🍑', - labelColor: '#35192b', - value: 'peach', - }, - { - color: '#fffad2', - label: 'Banana 🍌', - labelColor: '#555022', - value: 'banana', - }, - { - color: '#cbffd3', - label: 'Kiwi 🥝', - labelColor: '#2c4730', - value: 'kiwi', - }, -]; +function updateColor(newColor: string) { + color.value = newColor; +} -const favoriteFruit = ref(fruits[0]); +const preferredLanguages = ref([ + { label: 'Pink', hexColor: '#F49FBC' }, +]); diff --git a/packages/alto/src/components/Color/ColorPicker.vue b/packages/alto/src/components/Color/ColorPicker.vue index 5b0225c9..10385682 100644 --- a/packages/alto/src/components/Color/ColorPicker.vue +++ b/packages/alto/src/components/Color/ColorPicker.vue @@ -8,7 +8,7 @@ const props = withDefaults( defineProps(), { preserveTransparency: true, - swatches: () => ['#B8256EFF', '#25B86AFF', '#127EE3FF', '#F2990EFF', '#CC2929FF'], + swatches: () => ['#B8256E', '#25B86A', '#127EE3', '#F2990E', '#CC2929'], }, ); @@ -27,57 +27,61 @@ const alphaValue = ref(1); let isDragging = false; const setColor = (pickedColor: string) => { - if (props.preserveTransparency) { - const alpha = Math.floor(alphaValue.value * 255).toString(16).padStart(2, '0').toUpperCase(); - color.value = `${pickedColor}${alpha}`; - inputColor.value = `${pickedColor}${alpha}`; - } - else { - color.value = `${pickedColor}`; - inputColor.value = `${pickedColor}`; + let newColor = pickedColor; + + if (props.preserveTransparency || pickedColor.length === 6) { + const alpha = Math.floor(alphaValue.value * 255) + .toString(16) + .padStart(2, '0') + .toUpperCase(); + + newColor += alpha; } + + color.value = newColor; + inputColor.value = newColor; }; const setDraggableDivColor = (newX: number, newY: number) => { - const context = canvas.value!.getContext('2d'); - if (context) { - const x = newX === 0 ? newX : newX - 1; - const y = newY === 0 ? newY : newY - 1; - const pixelData = context.getImageData(x, y, 1, 1).data; - const red = pixelData[0].toString(16).padStart(2, '0').toUpperCase(); - const green = pixelData[1].toString(16).padStart(2, '0').toUpperCase(); - const blue = pixelData[2].toString(16).padStart(2, '0').toUpperCase(); - - const pickedColor = `#${red}${green}${blue}`; - setColor(pickedColor); - - emit('update:modelValue', color.value); + const context = canvas.value?.getContext('2d'); + if (!context) { + return; } + + const x = Math.max(newX - 1, 0); + const y = Math.max(newY - 1, 0); + const [r, g, b] = context.getImageData(x, y, 1, 1).data; + + const toHex = (value: number) => value.toString(16).padStart(2, '0').toUpperCase(); + const pickedColor = `#${toHex(r)}${toHex(g)}${toHex(b)}`; + + setColor(pickedColor); + emit('update:modelValue', color.value); }; const setDraggableDivCoordinates = (event: MouseEvent | TouchEvent) => { - if (draggableDiv.value) { - const rect = canvasContainer.value!.getBoundingClientRect(); - let x = 0; - let y = 0; - if (event instanceof MouseEvent) { - x = event.clientX - rect.left; - y = event.clientY - rect.top; - } - else if (event instanceof TouchEvent) { - x = event.touches[0].clientX - rect.left; - y = event.touches[0].clientY - rect.top; - } - const maxX = canvasContainer.value!.clientWidth; - const maxY = canvasContainer.value!.clientHeight; - const newX = Math.min(Math.max(0, x), maxX); - const newY = Math.min(Math.max(0, y), maxY); + const div = draggableDiv.value; + const container = canvasContainer.value; + if (!div || !container) { + return; + } - draggableDiv.value.style.left = `calc(${(newX / 224) * 100}% - 6px)`; - draggableDiv.value.style.top = `calc(${(newY / 224) * 100}% - 6px)`; + const rect = container.getBoundingClientRect(); + const { clientX, clientY } = 'touches' in event ? event.touches[0] : event; - setDraggableDivColor(newX, newY); - } + const x = clientX - rect.left; + const y = clientY - rect.top; + + const maxX = container.clientWidth; + const maxY = container.clientHeight; + + const newX = Math.min(Math.max(0, x), maxX); + const newY = Math.min(Math.max(0, y), maxY); + + div.style.left = `calc(${(newX / 224) * 100}% - 6px)`; + div.style.top = `calc(${(newY / 224) * 100}% - 6px)`; + + setDraggableDivColor(newX, newY); }; const startDrag = (event: MouseEvent | TouchEvent) => { @@ -145,39 +149,46 @@ onMounted(() => { emit('update:modelValue', props.modelValue); + const stopPropagation = (event: MouseEvent) => event.stopPropagation(); + if (props.preserveTransparency) { - alphaSlider.value.addEventListener('input', updateAlpha); - alphaSlider.value.addEventListener('touchmove', updateAlpha); - alphaSlider.value!.addEventListener('mousemove', (event: MouseEvent) => { - event.stopPropagation(); - }); + color.value = inputColor.value = `${props.modelValue}FF`; + + if (alphaSlider.value) { + alphaSlider.value.addEventListener('input', updateAlpha); + alphaSlider.value.addEventListener('touchmove', updateAlpha); + alphaSlider.value.addEventListener('mousemove', stopPropagation); + } + } + + if (colorSlider.value) { + colorSlider.value.addEventListener('input', updateColor); + colorSlider.value.addEventListener('touchmove', updateColor); + colorSlider.value.addEventListener('mousemove', stopPropagation); } - colorSlider.value!.addEventListener('input', updateColor); - colorSlider.value!.addEventListener('touchmove', updateColor); - colorSlider.value!.addEventListener('mousemove', (event: MouseEvent) => { - event.stopPropagation(); - }); window.addEventListener('mouseup', stopDrag); window.addEventListener('mousemove', drag); - canvasContainer.value!.addEventListener('touchmove', drag); - canvasContainer.value!.addEventListener('touchend', stopDrag); + if (canvasContainer.value) { + canvasContainer.value.addEventListener('touchmove', drag); + canvasContainer.value.addEventListener('touchend', stopDrag); + } }); onUnmounted(() => { - if (props.preserveTransparency) { + if (props.preserveTransparency && alphaSlider.value) { alphaSlider.value.removeEventListener('input', updateAlpha); alphaSlider.value.removeEventListener('touchmove', updateAlpha); } - colorSlider.value.removeEventListener('input', updateColor); - colorSlider.value.removeEventListener('touchmove', updateColor); + colorSlider.value?.removeEventListener('input', updateColor); + colorSlider.value?.removeEventListener('touchmove', updateColor); window.removeEventListener('mouseup', stopDrag); window.removeEventListener('mousemove', drag); - canvasContainer.value!.removeEventListener('touchmove', drag); - canvasContainer.value!.removeEventListener('touchend', stopDrag); + canvasContainer.value?.removeEventListener('touchmove', drag); + canvasContainer.value?.removeEventListener('touchend', stopDrag); }); @@ -211,6 +222,7 @@ onUnmounted(() => { width: 224px; height: 224px; overflow: hidden; + user-select: none; } .color-picker .canvas-container .draggable-div { @@ -292,7 +304,6 @@ onUnmounted(() => { transition: color 0.05s linear; border-radius: 0.3em; outline: none; - appearance: none; background-image: linear-gradient( 45deg, @@ -311,6 +322,7 @@ onUnmounted(() => { linear-gradient(to right, var(--gray-100) 25%, v-bind(color.slice(0, -2)) 85%); background-position: 0 0, 2px 2px, 0 0; background-size: 4px 4px, 4px 4px, 100% 100%; + appearance: none; } .color-picker .sliders .color-range { diff --git a/packages/alto/src/components/Color/utils.ts b/packages/alto/src/components/Color/utils.ts index 5e219b3a..9bc5d54a 100644 --- a/packages/alto/src/components/Color/utils.ts +++ b/packages/alto/src/components/Color/utils.ts @@ -90,7 +90,7 @@ export function hexToHue(hex: string) { export function getSliderValueFromColor(hexColorWithHash: string) { const hexColor = hexColorWithHash.startsWith('#') ? hexColorWithHash.slice(1) : hexColorWithHash; - let alpha = ''; + let alpha = 'FF'; if (hexColor.length === 8) { alpha = hexColor.slice(6, 8); } diff --git a/packages/alto/src/components/Tag/types.ts b/packages/alto/src/components/Tag/types.ts index 77712257..714336bf 100644 --- a/packages/alto/src/components/Tag/types.ts +++ b/packages/alto/src/components/Tag/types.ts @@ -1,4 +1,4 @@ -import type { DropdownCommonProps } from '../Dropdown/types'; +import type { DropdownValue } from '../Dropdown/types'; export type TagItemType = 'text' | 'color' | 'dropdown'; @@ -12,13 +12,18 @@ export interface UniqueTagItem extends TagItemValue { id: string } -export interface TagProps extends DropdownCommonProps { +export interface TagProps { modelValue: TagItemValue[] type?: TagItemType max?: number placeholder?: string disabled?: boolean error?: boolean + searchable?: boolean + searchInputPlaceholder?: string + search?: (value: string) => void + items?: DropdownValue[] + isLoading?: boolean } export interface TagItemProps {