@designbasekorea/ui 0.2.29 → 0.2.30
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/dist/index.esm.js +56 -62
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +56 -62
- package/dist/index.js.map +1 -1
- package/dist/index.umd.js +56 -62
- package/dist/index.umd.js.map +1 -1
- package/package.json +1 -1
package/dist/index.esm.js
CHANGED
|
@@ -5352,7 +5352,11 @@ const hsvToRgb = (h, s, v) => {
|
|
|
5352
5352
|
gp = 0;
|
|
5353
5353
|
bp = x;
|
|
5354
5354
|
}
|
|
5355
|
-
return {
|
|
5355
|
+
return {
|
|
5356
|
+
r: Math.round((rp + m) * 255),
|
|
5357
|
+
g: Math.round((gp + m) * 255),
|
|
5358
|
+
b: Math.round((bp + m) * 255),
|
|
5359
|
+
};
|
|
5356
5360
|
};
|
|
5357
5361
|
const rgbToHsl = (r, g, b) => {
|
|
5358
5362
|
r /= 255;
|
|
@@ -5377,11 +5381,15 @@ const rgbToHsl = (r, g, b) => {
|
|
|
5377
5381
|
}
|
|
5378
5382
|
h *= 60;
|
|
5379
5383
|
}
|
|
5380
|
-
return {
|
|
5384
|
+
return {
|
|
5385
|
+
h: Math.round(h * 100) / 100,
|
|
5386
|
+
s: Math.round(s * 10000) / 100,
|
|
5387
|
+
l: Math.round(l * 10000) / 100,
|
|
5388
|
+
};
|
|
5381
5389
|
};
|
|
5382
5390
|
/* ----------------------- 컴포넌트 ----------------------- */
|
|
5383
5391
|
const ColorPicker = ({ size = 'm', type = 'dropdown', position = 'bottom-left', value, defaultValue = '#006FFF', showInput = true, showAlpha = true, showFormatSelector = true, showCopyButton = true, disabled = false, readonly = false, onChange, onApply, onCancel, className, }) => {
|
|
5384
|
-
/** 내부
|
|
5392
|
+
/** 내부 HSV + alpha */
|
|
5385
5393
|
const [h, setH] = useState(211);
|
|
5386
5394
|
const [s, setS] = useState(100);
|
|
5387
5395
|
const [v, setV] = useState(50);
|
|
@@ -5389,15 +5397,13 @@ const ColorPicker = ({ size = 'm', type = 'dropdown', position = 'bottom-left',
|
|
|
5389
5397
|
const [isOpen, setIsOpen] = useState(false);
|
|
5390
5398
|
const [format, setFormat] = useState('hex');
|
|
5391
5399
|
const [isCopied, setIsCopied] = useState(false);
|
|
5392
|
-
// 하단 인풋(문자열) — 엔터로만 확정
|
|
5393
5400
|
const [colorInput, setColorInput] = useState('');
|
|
5394
5401
|
const [alphaInput, setAlphaInput] = useState('100');
|
|
5395
|
-
// 모달용 임시 상태 (취소 시 되돌리기)
|
|
5396
5402
|
const [tempColor, setTempColor] = useState('');
|
|
5397
5403
|
const pickerRef = useRef(null);
|
|
5398
5404
|
const areaRef = useRef(null);
|
|
5399
5405
|
const dragging = useRef(false);
|
|
5400
|
-
/**
|
|
5406
|
+
/** 초기값 세팅 */
|
|
5401
5407
|
useEffect(() => {
|
|
5402
5408
|
const initial = (value || defaultValue).toUpperCase();
|
|
5403
5409
|
const rgb = hexToRgb(initial);
|
|
@@ -5407,12 +5413,10 @@ const ColorPicker = ({ size = 'm', type = 'dropdown', position = 'bottom-left',
|
|
|
5407
5413
|
setS(s);
|
|
5408
5414
|
setV(v);
|
|
5409
5415
|
}
|
|
5410
|
-
// 인풋 초기 표현
|
|
5411
5416
|
setColorInput(initial);
|
|
5412
5417
|
setAlphaInput('100');
|
|
5413
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
5414
5418
|
}, []);
|
|
5415
|
-
/**
|
|
5419
|
+
/** 제어형(value) → HSV */
|
|
5416
5420
|
useEffect(() => {
|
|
5417
5421
|
if (!value)
|
|
5418
5422
|
return;
|
|
@@ -5424,11 +5428,11 @@ const ColorPicker = ({ size = 'm', type = 'dropdown', position = 'bottom-left',
|
|
|
5424
5428
|
setV(v);
|
|
5425
5429
|
}
|
|
5426
5430
|
}, [value]);
|
|
5427
|
-
/**
|
|
5431
|
+
/** RGB/HSL 계산 */
|
|
5428
5432
|
const rgb = useMemo(() => hsvToRgb(h, s, v), [h, s, v]);
|
|
5429
5433
|
const hex = useMemo(() => rgbToHex(rgb.r, rgb.g, rgb.b), [rgb]);
|
|
5430
5434
|
const hsl = useMemo(() => rgbToHsl(rgb.r, rgb.g, rgb.b), [rgb]);
|
|
5431
|
-
/**
|
|
5435
|
+
/** 출력값 formatted */
|
|
5432
5436
|
const formatted = useMemo(() => {
|
|
5433
5437
|
const alpha = a / 100;
|
|
5434
5438
|
switch (format) {
|
|
@@ -5440,22 +5444,24 @@ const ColorPicker = ({ size = 'm', type = 'dropdown', position = 'bottom-left',
|
|
|
5440
5444
|
default: return hex;
|
|
5441
5445
|
}
|
|
5442
5446
|
}, [format, hex, rgb, hsl, a]);
|
|
5443
|
-
/**
|
|
5447
|
+
/** ✅ 무한 루프 방지: formatted이 부모 value랑 같으면 onChange 호출하지 않음 */
|
|
5444
5448
|
useEffect(() => {
|
|
5449
|
+
if (value !== undefined && value.toUpperCase() === formatted.toUpperCase())
|
|
5450
|
+
return;
|
|
5445
5451
|
onChange?.(formatted);
|
|
5446
|
-
|
|
5447
|
-
}, [formatted]);
|
|
5452
|
+
}, [formatted, onChange, value]);
|
|
5448
5453
|
/** 드롭다운 외부 클릭 닫기 */
|
|
5449
5454
|
useEffect(() => {
|
|
5450
5455
|
const handler = (e) => {
|
|
5451
|
-
if (pickerRef.current && !pickerRef.current.contains(e.target))
|
|
5456
|
+
if (pickerRef.current && !pickerRef.current.contains(e.target)) {
|
|
5452
5457
|
setIsOpen(false);
|
|
5458
|
+
}
|
|
5453
5459
|
};
|
|
5454
5460
|
if (isOpen && type === 'dropdown')
|
|
5455
5461
|
document.addEventListener('mousedown', handler);
|
|
5456
5462
|
return () => document.removeEventListener('mousedown', handler);
|
|
5457
5463
|
}, [isOpen, type]);
|
|
5458
|
-
/**
|
|
5464
|
+
/** S/V 영역 업데이트 */
|
|
5459
5465
|
const updateFromArea = (clientX, clientY) => {
|
|
5460
5466
|
const el = areaRef.current;
|
|
5461
5467
|
if (!el)
|
|
@@ -5482,12 +5488,11 @@ const ColorPicker = ({ size = 'm', type = 'dropdown', position = 'bottom-left',
|
|
|
5482
5488
|
const onAlphaChange = useCallback((e) => {
|
|
5483
5489
|
const newAlpha = parseInt(e.target.value, 10);
|
|
5484
5490
|
setA(newAlpha);
|
|
5485
|
-
setAlphaInput(String(newAlpha));
|
|
5491
|
+
setAlphaInput(String(newAlpha));
|
|
5486
5492
|
}, []);
|
|
5487
|
-
/** 컬러
|
|
5493
|
+
/** 컬러 인풋 (엔터 확정) */
|
|
5488
5494
|
const commitColorInput = useCallback(() => {
|
|
5489
5495
|
const str = colorInput.trim();
|
|
5490
|
-
// HEX (#RRGGBB)
|
|
5491
5496
|
if (/^#([0-9A-Fa-f]{6})$/.test(str)) {
|
|
5492
5497
|
const rgb = hexToRgb(str);
|
|
5493
5498
|
const hsv = rgbToHsv(rgb.r, rgb.g, rgb.b);
|
|
@@ -5497,7 +5502,6 @@ const ColorPicker = ({ size = 'm', type = 'dropdown', position = 'bottom-left',
|
|
|
5497
5502
|
setColorInput(str.toUpperCase());
|
|
5498
5503
|
return;
|
|
5499
5504
|
}
|
|
5500
|
-
// rgb(r,g,b)
|
|
5501
5505
|
let m = /^rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)$/i.exec(str);
|
|
5502
5506
|
if (m) {
|
|
5503
5507
|
const r = clamp(parseInt(m[1], 10), 0, 255);
|
|
@@ -5510,7 +5514,6 @@ const ColorPicker = ({ size = 'm', type = 'dropdown', position = 'bottom-left',
|
|
|
5510
5514
|
setColorInput(`rgb(${r}, ${g}, ${b})`.toUpperCase());
|
|
5511
5515
|
return;
|
|
5512
5516
|
}
|
|
5513
|
-
// rgba(r,g,b,a)
|
|
5514
5517
|
m = /^rgba\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(0|1|0?\.\d+)\s*\)$/i.exec(str);
|
|
5515
5518
|
if (m) {
|
|
5516
5519
|
const r = clamp(parseInt(m[1], 10), 0, 255);
|
|
@@ -5526,18 +5529,14 @@ const ColorPicker = ({ size = 'm', type = 'dropdown', position = 'bottom-left',
|
|
|
5526
5529
|
setColorInput(`rgba(${r}, ${g}, ${b}, ${alpha})`.toUpperCase());
|
|
5527
5530
|
return;
|
|
5528
5531
|
}
|
|
5529
|
-
// 잘못된 값 → 원복
|
|
5530
5532
|
setColorInput(formatted.toUpperCase());
|
|
5531
5533
|
}, [colorInput, formatted]);
|
|
5532
5534
|
const onColorKeyDown = (e) => {
|
|
5533
5535
|
if (e.key === 'Enter')
|
|
5534
5536
|
commitColorInput();
|
|
5535
5537
|
};
|
|
5536
|
-
const onColorBlur = () =>
|
|
5537
|
-
|
|
5538
|
-
setColorInput(formatted.toUpperCase());
|
|
5539
|
-
};
|
|
5540
|
-
/** 알파 인풋: 엔터로만 확정 (0~100) */
|
|
5538
|
+
const onColorBlur = () => setColorInput(formatted.toUpperCase());
|
|
5539
|
+
/** 알파 인풋 (엔터 확정) */
|
|
5541
5540
|
const commitAlphaInput = useCallback(() => {
|
|
5542
5541
|
const n = Number(alphaInput.trim());
|
|
5543
5542
|
if (!Number.isNaN(n) && n >= 0 && n <= 100) {
|
|
@@ -5545,17 +5544,13 @@ const ColorPicker = ({ size = 'm', type = 'dropdown', position = 'bottom-left',
|
|
|
5545
5544
|
setAlphaInput(String(Math.round(n)));
|
|
5546
5545
|
return;
|
|
5547
5546
|
}
|
|
5548
|
-
// 잘못된 값 → 원복
|
|
5549
5547
|
setAlphaInput(String(a));
|
|
5550
5548
|
}, [alphaInput, a]);
|
|
5551
5549
|
const onAlphaInputKeyDown = (e) => {
|
|
5552
5550
|
if (e.key === 'Enter')
|
|
5553
5551
|
commitAlphaInput();
|
|
5554
5552
|
};
|
|
5555
|
-
const onAlphaInputBlur = () =>
|
|
5556
|
-
// 엔터로만 반영, 블러 시 원복
|
|
5557
|
-
setAlphaInput(String(a));
|
|
5558
|
-
};
|
|
5553
|
+
const onAlphaInputBlur = () => setAlphaInput(String(a));
|
|
5559
5554
|
/** 복사 */
|
|
5560
5555
|
const onCopy = useCallback(async () => {
|
|
5561
5556
|
try {
|
|
@@ -5579,18 +5574,15 @@ const ColorPicker = ({ size = 'm', type = 'dropdown', position = 'bottom-left',
|
|
|
5579
5574
|
setS(hsv.s);
|
|
5580
5575
|
setV(hsv.v);
|
|
5581
5576
|
}
|
|
5582
|
-
else {
|
|
5583
|
-
console.log('EyeDropper API is not supported');
|
|
5584
|
-
}
|
|
5585
5577
|
}
|
|
5586
5578
|
catch (e) {
|
|
5587
5579
|
console.error(e);
|
|
5588
5580
|
}
|
|
5589
5581
|
}, []);
|
|
5590
|
-
/**
|
|
5582
|
+
/** modal open(중요: formatted deps 제거) */
|
|
5591
5583
|
const handleModalOpen = useCallback(() => {
|
|
5592
5584
|
if (type === 'modal')
|
|
5593
|
-
setTempColor(hex);
|
|
5585
|
+
setTempColor(hex);
|
|
5594
5586
|
setIsOpen(true);
|
|
5595
5587
|
}, [type, hex]);
|
|
5596
5588
|
const handleModalApply = useCallback(() => {
|
|
@@ -5603,53 +5595,55 @@ const ColorPicker = ({ size = 'm', type = 'dropdown', position = 'bottom-left',
|
|
|
5603
5595
|
if (type === 'modal') {
|
|
5604
5596
|
const rgb = hexToRgb(tempColor);
|
|
5605
5597
|
if (rgb) {
|
|
5606
|
-
const
|
|
5607
|
-
setH(h);
|
|
5608
|
-
setS(s);
|
|
5609
|
-
setV(v);
|
|
5598
|
+
const hsv = rgbToHsv(rgb.r, rgb.g, rgb.b);
|
|
5599
|
+
setH(hsv.h);
|
|
5600
|
+
setS(hsv.s);
|
|
5601
|
+
setV(hsv.v);
|
|
5610
5602
|
}
|
|
5611
5603
|
onCancel?.();
|
|
5612
5604
|
setIsOpen(false);
|
|
5613
5605
|
}
|
|
5614
5606
|
}, [type, tempColor, onCancel]);
|
|
5615
|
-
/**
|
|
5607
|
+
/** 스타일 */
|
|
5616
5608
|
const hueTrackStyle = useMemo(() => ({
|
|
5617
5609
|
background: `linear-gradient(to right,
|
|
5618
|
-
|
|
5619
|
-
|
|
5620
|
-
|
|
5621
|
-
|
|
5622
|
-
|
|
5623
|
-
|
|
5624
|
-
|
|
5610
|
+
hsl(0,100%,50%),
|
|
5611
|
+
hsl(60,100%,50%),
|
|
5612
|
+
hsl(120,100%,50%),
|
|
5613
|
+
hsl(180,100%,50%),
|
|
5614
|
+
hsl(240,100%,50%),
|
|
5615
|
+
hsl(300,100%,50%),
|
|
5616
|
+
hsl(360,100%,50%))`,
|
|
5625
5617
|
}), []);
|
|
5626
|
-
// 상단 좌→우: 채도 증가 / 상단→하단: 밝기 감소
|
|
5627
5618
|
const areaBackground = useMemo(() => ({
|
|
5628
5619
|
backgroundImage: `
|
|
5629
|
-
|
|
5630
|
-
|
|
5631
|
-
|
|
5620
|
+
linear-gradient(to top, rgba(0,0,0,1), rgba(0,0,0,0)),
|
|
5621
|
+
linear-gradient(to right, #ffffff, hsl(${h}, 100%, 50%))
|
|
5622
|
+
`,
|
|
5632
5623
|
}), [h]);
|
|
5633
|
-
// ✔ 고정 흑백 알파 트랙 (색상 무관)
|
|
5634
5624
|
const alphaTrackStyle = useMemo(() => ({
|
|
5635
5625
|
backgroundImage: `
|
|
5636
|
-
|
|
5637
|
-
|
|
5638
|
-
|
|
5639
|
-
|
|
5640
|
-
|
|
5641
|
-
|
|
5626
|
+
linear-gradient(45deg, var(--db-border-base) 25%, transparent 25%),
|
|
5627
|
+
linear-gradient(-45deg, var(--db-border-base) 25%, transparent 25%),
|
|
5628
|
+
linear-gradient(45deg, transparent 75%, var(--db-border-base) 75%),
|
|
5629
|
+
linear-gradient(-45deg, transparent 75%, var(--db-border-base) 75%),
|
|
5630
|
+
linear-gradient(to right, rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0), rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 1))
|
|
5631
|
+
`,
|
|
5642
5632
|
backgroundSize: '8px 8px,8px 8px,8px 8px,8px 8px,100% 100%',
|
|
5643
5633
|
backgroundPosition: '0 0,0 4px,4px -4px,-4px 0,0 0',
|
|
5644
5634
|
backgroundColor: 'var(--db-surface-base)',
|
|
5645
|
-
}), []);
|
|
5635
|
+
}), [rgb]);
|
|
5646
5636
|
const classes = clsx('designbase-color-picker', `designbase-color-picker--${size}`, `designbase-color-picker--${position}`, {
|
|
5647
5637
|
'designbase-color-picker--disabled': disabled,
|
|
5648
5638
|
'designbase-color-picker--readonly': readonly,
|
|
5649
5639
|
'designbase-color-picker--open': isOpen,
|
|
5650
5640
|
'designbase-color-picker--no-input': !showInput,
|
|
5651
5641
|
}, className);
|
|
5652
|
-
const Trigger = (jsxs("div", { className: "designbase-color-picker__trigger", onClick: () => !disabled && !readonly && (type === 'modal' ? handleModalOpen() : setIsOpen(v => !v)), children: [jsx("div", { className: "designbase-color-picker__color-display", children: jsx("div", { className: "designbase-color-picker__color-box", style: {
|
|
5642
|
+
const Trigger = (jsxs("div", { className: "designbase-color-picker__trigger", onClick: () => !disabled && !readonly && (type === 'modal' ? handleModalOpen() : setIsOpen(v => !v)), children: [jsx("div", { className: "designbase-color-picker__color-display", children: jsx("div", { className: "designbase-color-picker__color-box", style: {
|
|
5643
|
+
backgroundColor: showAlpha
|
|
5644
|
+
? `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${a / 100})`
|
|
5645
|
+
: hex
|
|
5646
|
+
} }) }), showInput && (jsx("input", { type: "text", value: colorInput, onChange: (e) => setColorInput(e.target.value), onKeyDown: onColorKeyDown, onBlur: onColorBlur, onClick: (e) => e.stopPropagation(), disabled: disabled, readOnly: readonly, className: "designbase-color-picker__input", placeholder: "#000000" })), showInput && showCopyButton && (jsx("button", { type: "button", className: "designbase-color-picker__copy-button-inline", onClick: (e) => { e.stopPropagation(); onCopy(); }, disabled: disabled, "aria-label": "Copy color value", children: isCopied ? jsx(DoneIcon$1, { size: 14 }) : jsx(CopyIcon, { size: 14 }) })), jsx("button", { type: "button", className: "designbase-color-picker__toggle", disabled: disabled, "aria-label": "Toggle color picker", children: jsx(ChevronDownIcon, { size: 16 }) })] }));
|
|
5653
5647
|
const Selector = (jsxs("div", { className: "designbase-color-picker__selector", children: [jsx("div", { className: "designbase-color-picker__color-area", children: jsx("div", { ref: areaRef, className: "designbase-color-picker__color-field", style: areaBackground, onMouseDown: onAreaMouseDown, onMouseMove: onAreaMouseMove, onMouseUp: onAreaMouseUp, onMouseLeave: onAreaLeave, onTouchStart: onAreaTouchStart, onTouchMove: onAreaTouchMove, onTouchEnd: onAreaTouchEnd, children: jsx("div", { className: "designbase-color-picker__color-pointer", style: { left: `${s}%`, top: `${100 - v}%`, backgroundColor: `rgb(${rgb.r}, ${rgb.g}, ${rgb.b})` } }) }) }), jsxs("div", { className: "designbase-color-picker__controls", children: [jsx(Button, { variant: "tertiary", size: "m", iconOnly: true, onClick: onEyedrop, "aria-label": "Eyedropper tool", children: jsx(EyedropperIcon, {}) }), jsxs("div", { className: "designbase-color-picker__slider-container", children: [jsx("div", { className: "designbase-color-picker__hue-slider", children: jsx("input", { type: "range", min: 0, max: 360, value: h, onChange: onHueChange, className: "designbase-color-picker__slider designbase-color-picker__slider--hue", style: hueTrackStyle }) }), showAlpha && (jsx("div", { className: "designbase-color-picker__alpha-slider", children: jsx("input", { type: "range", min: 0, max: 100, value: a, onChange: onAlphaChange, className: "designbase-color-picker__slider designbase-color-picker__slider--alpha", style: alphaTrackStyle }) }))] })] }), jsxs("div", { className: "designbase-color-picker__value-display", children: [showFormatSelector && (jsx(Select, { value: format, onChange: (v) => setFormat(v), showClearButton: false, options: [
|
|
5654
5648
|
{ label: 'HEX', value: 'hex' },
|
|
5655
5649
|
{ label: 'RGB', value: 'rgb' },
|