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