PlColorPicker
눈으로 고르는 색입니다. 채도 사각형 옆에 색상 레일이 놓이는데, 모든 디자인 도구가 정착한 배치입니다. 한 색상의 모든 색이 포인터 한 번의 움직임 안에 들어오기 때문입니다.
import { PlColorPicker } from 'plass-ui';
<PlColorPicker label="Project colour" value={color} onValueChange={setColor} />;import 'package:plass_ui/plass_ui.dart';
PlColorPicker(
label: const Text('Project colour'),
value: colour,
onValueChanged: (String next) => setState(() => colour = next),
);Props
| Prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
| variant공통 | 'solid' | 'glass' | 'ghost' | 'glass' | 트리거의 재질. 폼 안의 다른 필드와 같은 껍데기입니다 |
| size공통 | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'md' | 트리거의 높이, 패널의 너비, 사각형과 레일의 크기 |
| color공통 | 'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'info' | 'primary' | 의미론적 색 역할. 임의 색상값은 받지 않습니다 |
| density공통 | 'default' | 'compact' | 'default' | 여백만 바꿉니다. 높이와 글자 크기는 그대로 |
| elevation공통 | 0 | 1 | 2 | 3 | 0 | 그림자 깊이. 0은 그림자 없음 |
| value | string | — | 색, CSS 문자열로. 직접 몰고 싶으면 넘기세요 |
| defaultValue | string | '#1a58d1' | uncontrolled 피커가 시작하는 색 |
| onValueChange | (value: string) => void | — | 새 색과 함께, format대로 쓰여서 |
| format | 'hex' | 'rgb' | 'hsl' | 'hex' | 나가는 값이 쓰이는 표기법 |
| alpha | boolean | false | 불투명도 레일을 내주고, 값이 네 번째 채널을 갖게 합니다 |
| swatches | readonly string[] | false | — | 패널 아래의 기성 색들. false면 그리지 않고, 배열이면 기본 세트를 대체합니다 |
| inline | boolean | false | 팝업 대신 페이지에 패널을 그립니다. 트리거는 없습니다 |
| editable | boolean | true | 값을 타이핑할 수 있는 패널 아래의 필드 |
| label | ReactNode | — | 컨트롤 위의 라벨 |
| description | ReactNode | — | 아래의 도움말 |
| error | ReactNode | — | 아래의 오류 메시지. 있으면 컨트롤이 invalid가 됩니다 |
| invalid | boolean | — | 메시지 없이 같은 상태로 |
| required | boolean | false | 라벨에 필수 표시를 붙입니다 |
| disabled | boolean | false | 쓸 수 없고 tab 순서에서 빠집니다 |
| readOnly | boolean | false | 색을 보여 주고 바꾸지 못하게 합니다 |
| fullWidth | boolean | false | 트리거를 컨테이너까지 늘립니다 |
| clearable | boolean | false | 컨트롤을 비우는 ×를 내줍니다 |
| name | string | — | 이 이름으로 폼과 함께 제출됩니다 |
| open | boolean | — | 팝업이 열려 있는지 |
| defaultOpen | boolean | false | uncontrolled 팝업이 시작하는 상태 |
| onOpenChange | (open: boolean) => void | — | 팝업이 열리거나 닫힐 때 |
| labels | Partial<PlColorPickerLabels> | — | 글자가 없는 부분들의 접근 가능한 이름을 하나씩 덮어씁니다 |
| Prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
| variant공통 | PlassVariant | PlassVariant.glass | 트리거의 재질. 폼 안의 다른 필드와 같은 껍데기입니다 |
| size공통 | PlassSize | PlassSize.md | 트리거의 높이, 패널의 너비, 사각형과 레일의 크기 |
| color공통 | PlassColor | PlassColor.primary | 의미론적 색 역할. 임의 색상값은 받지 않습니다 |
| density공통 | PlassDensity | PlassDensity.standard | 여백만 바꿉니다. 높이와 글자 크기는 그대로 |
| elevation공통 | int | 0 | 그림자 깊이. 0은 그림자 없음 |
| value | String? | — | 색, CSS 문자열로. null이면 피커 자신의 파랑에서 시작합니다 |
| onValueChanged | ValueChanged<String>? | — | 새 색과 함께, format대로 쓰여서 |
| format | PlColorFormat | PlColorFormat.hex | 나가는 값이 쓰이는 표기법 |
| alpha | bool | false | 불투명도 레일을 내주고, 값이 네 번째 채널을 갖게 합니다 |
| swatches | List<String> | defaultSwatches | 패널 아래의 기성 색들. 빈 리스트면 그리지 않습니다 |
| inline | bool | false | 팝업 대신 페이지에 패널을 그립니다. 트리거는 없습니다 |
| editable | bool | true | 값을 타이핑할 수 있는 패널 아래의 필드 |
| label | Widget? | — | 컨트롤 위의 라벨 |
| description | Widget? | — | 아래의 도움말 |
| error | Widget? | — | 아래의 오류 메시지. 있으면 컨트롤이 invalid가 됩니다 |
| invalid | bool? | — | 메시지 없이 같은 상태로 |
| disabled | bool | false | 쓸 수 없고 tab 순서에서 빠집니다 |
| readOnly | bool | false | 색을 보여 주고 바꾸지 못하게 합니다 |
| fullWidth | bool | false | 트리거를 컨테이너까지 늘립니다 |
| clearable | bool | false | 컨트롤을 비우는 ×를 내줍니다 |
| labels | PlColorPickerLabels | const PlColorPickerLabels() | 글자가 없는 부분들의 접근 가능한 이름을 하나씩 덮어씁니다 |
네이티브 <div> 속성은 모두 wrapper로 그대로 전달됩니다. color는 여기서 Plass의 prop이라 제외됩니다(컨트롤이 켜지는 계열이지, 담고 있는 색이 아닙니다). 그리고 defaultValue와 onChange는 value와 onValueChange로 표기하기 때문입니다.
공용 축이 라이브러리 전체에서 무엇을 뜻하는지는 prop 규약에 있습니다.
HSV가 모델이고, 거기서 나가지 않습니다
패널의 상태는 색상·채도·명도입니다. 문자열은 거기서 파생되고, 반대는 결코 아닙니다.
취향의 문제가 아닙니다. RGB를 거치면 검정의 모든 음영이 같은 색입니다(#000000에는 되읽을 색상이 없습니다). 그래서 자기 출력을 다시 파싱하는 피커는 포인터가 사각형 바닥에 닿는 순간 색상 레일을 빨강으로 튕겨 버립니다. 모델을 쥐고 있는 것이 레일을 가만히 있게 합니다.
들어오는 value는 모델이 이미 뜻하는 것과 다를 때만 모델을 다시 심습니다. 그리고 "다름"은 문자열이 아니라 색으로 비교합니다. #FF0000과 #ff0000은 두 번 쓰인 같은 색이고, 문자열 비교라면 방금 자기가 만들어 낸 값으로 매 렌더마다 영원히 모델을 다시 심게 됩니다.
Examples
inline
트리거 없이 페이지에 패널을 그립니다. 색이 열 개 중 한 필드가 아니라 편집 대상 자체인 사이드바나 설정 창을 위한 것입니다.
format
나가는 값이 어떤 표기법으로 쓰이는지입니다: hex, rgb, hsl.
셋 다 색이 불투명하면 alpha를 뺍니다. alpha를 켠 적 없는 호출자가 세 채널만 쓴 컨트롤에서 rgba(…, 1)을 보게 되면 안 되기 때문입니다.
alpha
세 번째 레일을 더하고 값이 네 번째 채널을 갖게 합니다. 레일은 체커보드 위에 그려지고, 체커는 conic 그러데이션 둘이 아니라 45°의 linear stop 넷입니다. conic으로 그린 체커는 소수 device pixel ratio에서 모든 타일 한가운데에 이음매가 생깁니다.
swatches
제품이 실제로 쓰는 몇 개의 색을 클릭 한 번 거리에 둡니다. 배열을 넘기면 기본 세트를 대체하고, false면 아무것도 그리지 않습니다.
기본 세트는 스펙트럼에 회색을 더한 것이고, 일부러 라이브러리의 여섯 계열이 아닙니다. 그것들은 의미론적 역할이고, 피커에는 의미가 아니라 색을 요청하기 때문입니다.
선택된 스와치의 체크는 상대 휘도로 정해진 검정 또는 흰색입니다. 고정된 흰 체크는 노랑 위에서 사라지고, 밝기만 보면 초록에서 반대로 놓입니다.
readOnly · disabled · error
error는 컨트롤을 invalid로 만들고, 그러면 색 계열 전체가 danger로 넘어갑니다. 가장자리, 링, 메시지가 함께 뒤집힙니다. invalid는 메시지 없이 같은 일을 합니다.
readOnly 피커는 색을 보여 주고 아무것도 받지 않습니다. 레일은 값을 지키고 tab stop을 잃습니다. disabled는 tab 순서에서 빠집니다.
아래에 색 라이브러리가 없습니다
변환은 internal/color.ts입니다. HSV, RGB, HSL과 파서 하나, 포매터 하나. 삼각함수 없는 산수 백 줄 남짓입니다. 색을 계산하는 컴포넌트가 그걸 해 주는 의존성 없이 배포되는 이유가 전부 그것입니다.
읽는 것: 네 가지 길이의 hex, 그리고 콤마와 공백 문법 양쪽의 rgb()/rgba()/hsl()/hsla(). 일부러 읽지 않는 것: 이름 있는 색과 color(). 피커는 읽을 수 있는 모든 값을 쓸 수도 있어야 하는데, rebeccapurple에서 패널 위의 한 점으로 정직하게 돌아올 길이 없습니다.
React 빌드와 다른 점
| React | Flutter | 이유 |
|---|---|---|
value / defaultValue | nullable인 value | null은 "피커 자신의 파랑"입니다. 첫 변경 이후로는 문자열이 호출자의 것이고, 이 패키지의 다른 모든 필드가 그렇습니다. |
swatches: false | swatches: [] | 빈 리스트가 두 번째 타입 없이 같은 말을 합니다. |
open / defaultOpen / onOpenChange | — | 팝업은 피커 자신의 것이고, 그것을 붙들어야 하는 route guard 같은 모양이 여기에는 없습니다. |
name과 hidden input | — | 참여할 네이티브 폼 제출이 없습니다. |
| linear 그러데이션 넷으로 만든 체커 | painter | CustomPainter에는 피할 이음매도, 싸울 타일링도 없습니다. |
partial인 labels | 기본값이 붙은 클래스 PlColorPickerLabels | Dart는 선택적 필드에 이름을 붙입니다. 레코드의 partial 같은 것은 없습니다. |
className, style | — | 전달할 class 목록도 style 속성도 없습니다. |
Accessibility
- 사각형과 각 레일은
aria-valuenow를 지닌 진짜slider이고 화살표 키로 움직입니다. 한 단계, Shift와 함께면 열 단계. 라이브러리의 모든 슬라이더가 쓰는 같은 한 쌍입니다. - 사각형은 두 채널을 함께 보고합니다.
aria-valuenow는 채도이고aria-valuetext는"채도%, 명도%"입니다. 숫자 하나로는 평면 위의 한 점을 설명할 수 없기 때문입니다. - 색상 레일은 멈추지 않고 감깁니다. 빨강에서 한 단계 뒤는 0°가 아니라 358°입니다. 색상환은 원이고 레일은 그것의 그림입니다.
- 피커가 답하지 않는 키는 건드리지 않으므로, Tab이 그러데이션에 삼켜지지 않고 지나갑니다.
- 모든 스와치는 자기 색으로 이름 붙은 진짜
<button>이고, 선택된 것에aria-pressed가 붙습니다. labels는 글자가 없는 부분들의 이름을 하나씩 바꿉니다. 기본적으로 전부 영어로 이름이 붙어 있습니다.- 드래그는 요소에서 pointer capture를 가져가므로, 드래그 중 포인터가 패널을 벗어나도 색이 계속 바뀝니다.