@loword/loword-design-system 0.0.15 → 0.0.17
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/components/LDCarousel/CarouselBase.d.ts.map +1 -1
- package/dist/components/LDCarousel/CarouselBase.js +8 -2
- package/dist/components/LDCarousel/LDCarousel.d.ts.map +1 -1
- package/dist/components/LDCarousel/LDCarousel.js +3 -5
- package/dist/components/LDChart/ChartBase.d.ts +14 -0
- package/dist/components/LDChart/ChartBase.d.ts.map +1 -0
- package/dist/components/LDChart/ChartBase.js +19 -0
- package/dist/components/LDChart/LDChart.d.ts +47 -0
- package/dist/components/LDChart/LDChart.d.ts.map +1 -0
- package/dist/components/LDChart/LDChart.js +183 -0
- package/dist/components/LDDaumPostButton/LDDaumPostButton.d.ts +18 -0
- package/dist/components/LDDaumPostButton/LDDaumPostButton.d.ts.map +1 -0
- package/dist/components/LDDaumPostButton/LDDaumPostButton.js +32 -0
- package/dist/components/LDSegmented/LDSegmented.d.ts +29 -0
- package/dist/components/LDSegmented/LDSegmented.d.ts.map +1 -0
- package/dist/components/LDSegmented/LDSegmented.js +121 -0
- package/dist/components/LDSegmented/SegmentedBase.d.ts +33 -0
- package/dist/components/LDSegmented/SegmentedBase.d.ts.map +1 -0
- package/dist/components/LDSegmented/SegmentedBase.js +136 -0
- package/dist/components/LDSegmented/useIsomorphicLayoutEffect.d.ts +6 -0
- package/dist/components/LDSegmented/useIsomorphicLayoutEffect.d.ts.map +1 -0
- package/dist/components/LDSegmented/useIsomorphicLayoutEffect.js +5 -0
- package/dist/components/LDSelect/SelectBase.d.ts.map +1 -1
- package/dist/components/LDSelect/SelectBase.js +2 -1
- package/dist/components/LDTable/LDTanstackTable.js +2 -2
- package/dist/components/LDTabs/LDTabs.d.ts.map +1 -1
- package/dist/components/LDTabs/LDTabs.js +358 -211
- package/dist/components/LDTextarea/LDFormTextarea.d.ts.map +1 -1
- package/dist/components/LDTextarea/LDFormTextarea.js +1 -2
- package/dist/index.css +126 -35
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/utils/tv.d.ts +125 -0
- package/dist/utils/tv.d.ts.map +1 -0
- package/dist/utils/tv.js +91 -0
- package/dist/utils/utils.d.ts +4 -0
- package/dist/utils/utils.d.ts.map +1 -1
- package/dist/utils/utils.js +20 -0
- package/package.json +16 -1
- package/src/styles/carousel.css +28 -15
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CarouselBase.d.ts","sourceRoot":"","sources":["../../../src/components/LDCarousel/CarouselBase.tsx"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,SAAS,EAMf,MAAM,OAAO,CAAC;AACf,OAAO,gBAAgB,MAAM,sBAAsB,CAAC;AAGpD,OAAO,KAAK,EACV,iBAAiB,EACjB,gBAAgB,EAEjB,MAAM,gBAAgB,CAAC;AAUxB,MAAM,MAAM,mBAAmB,GAAG,YAAY,GAAG,UAAU,CAAC;AAE5D,UAAU,cAAc;IACtB,4BAA4B;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2CAA2C;IAC3C,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,UAAU,gBAAgB;IACxB,2BAA2B;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,0BAA0B;IAC1B,SAAS,CAAC,EAAE,SAAS,GAAG,UAAU,CAAC;IACnC,sCAAsC;IACtC,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,gCAAgC;IAChC,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAGD,UAAU,mBAAmB;IAC3B,QAAQ,EAAE,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC;IACjD,QAAQ,EAAE,iBAAiB,GAAG,SAAS,CAAC;IACxC,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,aAAa,EAAE,OAAO,CAAC;IACvB,aAAa,EAAE,OAAO,CAAC;IACvB,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,WAAW,EAAE,mBAAmB,CAAC;IACjC,mEAAmE;IACnE,GAAG,EAAE,MAAM,CAAC;CACb;AAaD,+CAA+C;AAC/C,eAAO,MAAM,WAAW,2BAAqB,CAAC;AAG9C,MAAM,WAAW,SAAS;IACxB,WAAW,CAAC,EAAE,mBAAmB,CAAC;IAClC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,uCAAuC;IACvC,QAAQ,CAAC,EAAE,OAAO,GAAG,cAAc,CAAC;IACpC;qEACiE;IACjE,UAAU,CAAC,EAAE,OAAO,GAAG,gBAAgB,CAAC;IACxC,yDAAyD;IACzD,KAAK,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAC;IACnC;4EACwE;IACxE,aAAa,CAAC,EAAE,WAAW,GAAG,WAAW,GAAG,KAAK,CAAC;IAClD,+CAA+C;IAC/C,OAAO,CAAC,EAAE,IAAI,CAAC,gBAAgB,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,eAAe,CAAC,CAAC;IAC9E,2BAA2B;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;
|
|
1
|
+
{"version":3,"file":"CarouselBase.d.ts","sourceRoot":"","sources":["../../../src/components/LDCarousel/CarouselBase.tsx"],"names":[],"mappings":"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"}
|
|
@@ -58,13 +58,15 @@ const Root = ({ orientation = 'horizontal', loop = false, autoplay = false, auto
|
|
|
58
58
|
setScrollSnaps(emblaApi.scrollSnapList());
|
|
59
59
|
//슬라이드 초기 상태 설정
|
|
60
60
|
onSelect(emblaApi);
|
|
61
|
+
const onReInitSnaps = () => setScrollSnaps(emblaApi.scrollSnapList());
|
|
61
62
|
emblaApi.on('select', onSelect);
|
|
62
63
|
emblaApi.on('reInit', onSelect);
|
|
63
|
-
emblaApi.on('reInit',
|
|
64
|
+
emblaApi.on('reInit', onReInitSnaps);
|
|
64
65
|
return () => {
|
|
65
66
|
//클린업 함수
|
|
66
67
|
emblaApi.off('select', onSelect);
|
|
67
68
|
emblaApi.off('reInit', onSelect);
|
|
69
|
+
emblaApi.off('reInit', onReInitSnaps);
|
|
68
70
|
};
|
|
69
71
|
}, [emblaApi, onSelect]);
|
|
70
72
|
const scrollPrev = useCallback(() => emblaApi?.scrollPrev(), [emblaApi]);
|
|
@@ -127,7 +129,11 @@ const Next = ({ className, children, ...props }) => {
|
|
|
127
129
|
};
|
|
128
130
|
const TopArrows = ({ className, children, ...props }) => {
|
|
129
131
|
const { scrollNext, scrollPrev, canScrollNext, canScrollPrev, orientation } = useCarouselContext();
|
|
130
|
-
return (
|
|
132
|
+
return (
|
|
133
|
+
// ld-carousel__nav-top: 상단 화살표 영역의 고정 훅.
|
|
134
|
+
// side 화살표의 .ld-carousel__nav 와 같은 역할 — 소비측에서 반응형 제어(예: 모바일에서만 숨김)를
|
|
135
|
+
// 할 때 마크업 구조(자식 순서 등)에 기대지 않도록 클래스를 제공한다.
|
|
136
|
+
_jsxs("div", { className: "ld-carousel__nav-top flex flex-row ml-auto", children: [_jsx("button", { type: "button", onClick: scrollPrev, disabled: !canScrollPrev, "aria-label": props['aria-label'] ?? '이전', className: cn('disabled:opacity-40 disabled:cursor-not-allowed cursor-pointer rounded-full size-8 flex justify-center items-center ld-transition', className), children: children ?? _jsx(ChevronLeft, { size: 22, strokeWidth: 1.2 }) }), _jsx("button", { type: "button", onClick: scrollNext, disabled: !canScrollNext, "aria-label": props['aria-label'] ?? '다음', className: cn('disabled:opacity-40 disabled:cursor-not-allowed cursor-pointer rounded-full size-8 flex justify-center items-center ld-transition', className), children: children ?? _jsx(ChevronRight, { size: 22, strokeWidth: 1.2 }) })] }));
|
|
131
137
|
};
|
|
132
138
|
const Dots = ({ className, dotClassName }) => {
|
|
133
139
|
const { scrollSnaps, selectedIndex, scrollTo } = useCarouselContext();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LDCarousel.d.ts","sourceRoot":"","sources":["../../../src/components/LDCarousel/LDCarousel.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAY,KAAK,aAAa,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AACrE,OAAqB,EACnB,KAAK,mBAAmB,EACxB,KAAK,SAAS,EACf,MAAM,gBAAgB,CAAC;AAGxB,MAAM,WAAW,eAAe;IAC9B,uBAAuB;IACvB,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,gCAAgC;IAChC,WAAW,CAAC,EAAE,mBAAmB,CAAC;IAClC,sBAAsB;IACtB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,sDAAsD;IACtD,QAAQ,CAAC,EAAE,OAAO,GAAG;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,iBAAiB,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IACrE,0DAA0D;IAC1D,UAAU,CAAC,EAAE,SAAS,CAAC,YAAY,CAAC,CAAC;IACrC,sCAAsC;IACtC,KAAK,CAAC,EAAE,SAAS,CAAC,OAAO,CAAC,CAAC;IAC3B,8FAA8F;IAC9F,aAAa,CAAC,EAAE,SAAS,CAAC,eAAe,CAAC,CAAC;IAC3C;+EAC2E;IAC3E,OAAO,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC/B,8DAA8D;IAC9D,aAAa,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;IACxC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,gCAAgC;IAChC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,yDAAyD;IACzD,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAChC,mCAAmC;IACnC,IAAI,CAAC,EAAE;QACL;8DACsD;QACtD,IAAI,EAAE,MAAM,CAAC;QACb,8EAA8E;QAC9E,IAAI,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC;KACtB,CAAC;IACF,4CAA4C;IAC5C,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,wCAAwC;IACxC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;GAGG;AACH,QAAA,MAAM,UAAU;+MAmBb,eAAe;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"LDCarousel.d.ts","sourceRoot":"","sources":["../../../src/components/LDCarousel/LDCarousel.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAY,KAAK,aAAa,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AACrE,OAAqB,EACnB,KAAK,mBAAmB,EACxB,KAAK,SAAS,EACf,MAAM,gBAAgB,CAAC;AAGxB,MAAM,WAAW,eAAe;IAC9B,uBAAuB;IACvB,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,gCAAgC;IAChC,WAAW,CAAC,EAAE,mBAAmB,CAAC;IAClC,sBAAsB;IACtB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,sDAAsD;IACtD,QAAQ,CAAC,EAAE,OAAO,GAAG;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,iBAAiB,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IACrE,0DAA0D;IAC1D,UAAU,CAAC,EAAE,SAAS,CAAC,YAAY,CAAC,CAAC;IACrC,sCAAsC;IACtC,KAAK,CAAC,EAAE,SAAS,CAAC,OAAO,CAAC,CAAC;IAC3B,8FAA8F;IAC9F,aAAa,CAAC,EAAE,SAAS,CAAC,eAAe,CAAC,CAAC;IAC3C;+EAC2E;IAC3E,OAAO,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC/B,8DAA8D;IAC9D,aAAa,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;IACxC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,gCAAgC;IAChC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,yDAAyD;IACzD,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAChC,mCAAmC;IACnC,IAAI,CAAC,EAAE;QACL;8DACsD;QACtD,IAAI,EAAE,MAAM,CAAC;QACb,8EAA8E;QAC9E,IAAI,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC;KACtB,CAAC;IACF,4CAA4C;IAC5C,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,wCAAwC;IACxC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;GAGG;AACH,QAAA,MAAM,UAAU;+MAmBb,eAAe;;;;;;;;;;CAyFjB,CAAC;AAYF,eAAe,UAAU,CAAC"}
|
|
@@ -14,11 +14,9 @@ const LDCarousel = ({ slides, orientation = 'horizontal', loop = false, autoplay
|
|
|
14
14
|
const columns = rows <= 1
|
|
15
15
|
? slides.map((_, i) => [i])
|
|
16
16
|
: Array.from({ length: columnCount }, (_, col) => Array.from({ length: rows }, (_, row) => grid?.fill === 'col' ? col * rows + row : row * columnCount + col).filter((i) => i < slides.length));
|
|
17
|
-
return (_jsx(CarouselBase.Root, { orientation: orientation, loop: loop, autoplay: autoplay, autoScroll: autoScroll, align: align, containScroll: containScroll, options: options, gap: gap, className: className, style: style, children: _jsxs("div", { className: "flex flex-col gap-3", children: [arrowPosition === 'top' && _jsx(CarouselBase.TopArrows, {}), _jsxs("div", { className: cn('flex items-center gap-3 w-full relative', isVertical && 'flex-col'
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
// items-start: 위치만 맞추고 카드 높이는 콘텐츠대로 둔다.
|
|
21
|
-
_jsx("div", { className: "grid h-full items-start", style: {
|
|
17
|
+
return (_jsx(CarouselBase.Root, { orientation: orientation, loop: loop, autoplay: autoplay, autoScroll: autoScroll, align: align, containScroll: containScroll, options: options, gap: gap, className: cn(arrowPosition === 'side' && !isVertical && 'ld-carousel--nav-gutter-x', className), style: style, children: _jsxs("div", { className: "flex flex-col gap-3", children: [arrowPosition === 'top' && _jsx(CarouselBase.TopArrows, {}), _jsxs("div", { className: cn('flex items-center gap-3 w-full relative', isVertical && 'flex-col', isVertical &&
|
|
18
|
+
arrowPosition === 'side' &&
|
|
19
|
+
'ld-carousel__nav-clearance-y'), children: [arrowPosition === 'side' && _jsx(CarouselBase.Prev, {}), _jsx(CarouselBase.Viewport, { className: cn('w-full', viewportClassName), children: _jsx(CarouselBase.Container, { children: columns.map((column, i) => (_jsx(CarouselBase.Slide, { slidesPerView: slidesPerView, index: i, total: columns.length, className: slideClassName, children: rows <= 1 ? (slides[i]) : (_jsx("div", { className: "grid h-full items-start", style: {
|
|
22
20
|
gridTemplateRows: `repeat(${rows}, minmax(0, 1fr))`,
|
|
23
21
|
rowGap: gap,
|
|
24
22
|
}, children: column.map((slideIndex) => (_jsx(Fragment, { children: slides[slideIndex] }, slideIndex))) })) }, i))) }) }), arrowPosition === 'side' && _jsx(CarouselBase.Next, {})] }), (showDots || showCounter) && (_jsxs("div", { className: "flex items-center justify-center gap-3", children: [showDots && (_jsx(CarouselBase.Dots, { className: isVertical ? 'flex-col' : undefined })), showCounter && _jsx(CarouselBase.Counter, {})] }))] }) }));
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type ReactNode, type Ref } from 'react';
|
|
2
|
+
interface ChartSlotProps {
|
|
3
|
+
className?: string;
|
|
4
|
+
style?: React.CSSProperties;
|
|
5
|
+
children?: ReactNode;
|
|
6
|
+
ref?: Ref<HTMLDivElement>;
|
|
7
|
+
}
|
|
8
|
+
export declare const ChartBase: {
|
|
9
|
+
Root: ({ className, style, children, ref }: ChartSlotProps) => import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
Viewport: ({ className, style, children, ref }: ChartSlotProps) => import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
Overlay: ({ className, style, children, ref }: ChartSlotProps) => import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
};
|
|
13
|
+
export default ChartBase;
|
|
14
|
+
//# sourceMappingURL=ChartBase.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ChartBase.d.ts","sourceRoot":"","sources":["../../../src/components/LDChart/ChartBase.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,GAAG,EAAE,MAAM,OAAO,CAAC;AAKjD,UAAU,cAAc;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAC5B,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,GAAG,CAAC,EAAE,GAAG,CAAC,cAAc,CAAC,CAAC;CAC3B;AA6BD,eAAO,MAAM,SAAS;gDA1B6B,cAAc;oDASV,cAAc;mDASf,cAAc;CAYnE,CAAC;AAEF,eAAe,SAAS,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
// ── Root: 최외곽 래퍼 ──
|
|
3
|
+
const Root = ({ className, style, children, ref }) => {
|
|
4
|
+
return (_jsx("div", { ref: ref, style: style, className: className, children: children }));
|
|
5
|
+
};
|
|
6
|
+
// ── Viewport: canvas와 오버레이를 담는 차트 영역 ──
|
|
7
|
+
const Viewport = ({ className, style, children, ref }) => {
|
|
8
|
+
return (_jsx("div", { ref: ref, style: style, className: className, children: children }));
|
|
9
|
+
};
|
|
10
|
+
// ── Overlay: Viewport 위에 겹쳐 그리는 레이어 (로딩 표시 등) ──
|
|
11
|
+
const Overlay = ({ className, style, children, ref }) => {
|
|
12
|
+
return (_jsx("div", { ref: ref, style: style, className: className, children: children }));
|
|
13
|
+
};
|
|
14
|
+
export const ChartBase = {
|
|
15
|
+
Root,
|
|
16
|
+
Viewport,
|
|
17
|
+
Overlay,
|
|
18
|
+
};
|
|
19
|
+
export default ChartBase;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/** label 자동 생성 단위. dayjs의 ManipulateType 중 아래 포맷이 정의된 3개만 허용한다. */
|
|
2
|
+
export type LDChartUnit = 'day' | 'month' | 'year';
|
|
3
|
+
export type LDChartPosition = 'left' | 'right';
|
|
4
|
+
/** 시리즈 색상 프리셋. DS 색 팔레트의 500 단계에 대응한다. */
|
|
5
|
+
export type LDChartColor = 'lowordBlue' | 'blue' | 'red' | 'orange' | 'yellow' | 'green' | 'pink' | 'purple';
|
|
6
|
+
/**
|
|
7
|
+
* LDChart의 props입니다.
|
|
8
|
+
* 원본(mono repo LDChart) 대비 `loader?: string`은 `loading?: boolean`으로,
|
|
9
|
+
* 임의 hex를 받던 `bgColor?: string`은 프리셋 `color?: LDChartColor`로 대체되고,
|
|
10
|
+
* 미사용이던 `onChange` prop은 제거되었습니다.
|
|
11
|
+
* 색상 기본값이 프리셋 `'lowordBlue'`로 통일되어, 원본의 기본 렌더와는 다릅니다
|
|
12
|
+
* (라인 `#5F92FF` → `#6891f8`, 3색 그라디언트 → 단일 색 알파 램프, hover 배경 `#EF4545` → 시리즈 색).
|
|
13
|
+
*/
|
|
14
|
+
export interface LDChartProps {
|
|
15
|
+
/** 차트에 그릴 단일 데이터셋(시리즈 이름, 값, 라벨) */
|
|
16
|
+
dataList: {
|
|
17
|
+
/** 범례/툴팁에 표시할 시리즈 이름 (기본값: 빈 문자열) */
|
|
18
|
+
keyword?: string;
|
|
19
|
+
/** y축 값 배열 */
|
|
20
|
+
value: number[];
|
|
21
|
+
/** x축 라벨 배열. 비어 있으면 unit 기준 오늘까지 7개 라벨을 자동 생성 */
|
|
22
|
+
label: string[];
|
|
23
|
+
};
|
|
24
|
+
/** 라인·포인트·그라디언트에 쓸 시리즈 색상 프리셋 (기본값: 'lowordBlue') */
|
|
25
|
+
color?: LDChartColor;
|
|
26
|
+
/** y축 위치 (기본값: 'right') */
|
|
27
|
+
position?: LDChartPosition;
|
|
28
|
+
/** true면 y축을 반전하고 채움 방향을 위쪽(start)으로 바꾼다 */
|
|
29
|
+
reverseYAxis?: boolean;
|
|
30
|
+
/** y축 눈금 뒤에 붙일 단위 문자열 */
|
|
31
|
+
yUnit?: string;
|
|
32
|
+
/** 툴팁 값 뒤에 붙일 단위 문자열 */
|
|
33
|
+
tooltipUnit?: string;
|
|
34
|
+
/** label이 비어 있을 때 자동 생성할 라벨의 단위 (기본값: 'day') */
|
|
35
|
+
unit?: LDChartUnit;
|
|
36
|
+
/** 로딩 오버레이 표시 여부 */
|
|
37
|
+
loading?: boolean;
|
|
38
|
+
/** 최외곽 래퍼(Root)에 적용할 className. 배경색은 기본 투명이므로 여기서 지정한다 */
|
|
39
|
+
rootClassName?: string;
|
|
40
|
+
/** 차트 영역(Viewport)에 적용할 className */
|
|
41
|
+
className?: string;
|
|
42
|
+
/** sizing(width/height/min·max)은 차트 영역(Viewport)에, 나머지 스타일은 Root에 적용된다 */
|
|
43
|
+
style?: React.CSSProperties;
|
|
44
|
+
}
|
|
45
|
+
declare const LDChart: ({ dataList, color, position, reverseYAxis, yUnit, tooltipUnit, unit, loading, rootClassName, className, style, }: LDChartProps) => import("react/jsx-runtime").JSX.Element;
|
|
46
|
+
export default LDChart;
|
|
47
|
+
//# sourceMappingURL=LDChart.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LDChart.d.ts","sourceRoot":"","sources":["../../../src/components/LDChart/LDChart.tsx"],"names":[],"mappings":"AAwCA,mEAAmE;AACnE,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG,OAAO,GAAG,MAAM,CAAC;AAQnD,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,OAAO,CAAC;AAE/C,0CAA0C;AAC1C,MAAM,MAAM,YAAY,GACpB,YAAY,GACZ,MAAM,GACN,KAAK,GACL,QAAQ,GACR,QAAQ,GACR,OAAO,GACP,MAAM,GACN,QAAQ,CAAC;AAyCb;;;;;;;GAOG;AACH,MAAM,WAAW,YAAY;IAC3B,oCAAoC;IACpC,QAAQ,EAAE;QACR,qCAAqC;QACrC,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,cAAc;QACd,KAAK,EAAE,MAAM,EAAE,CAAC;QAChB,iDAAiD;QACjD,KAAK,EAAE,MAAM,EAAE,CAAC;KACjB,CAAC;IACF,qDAAqD;IACrD,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB,2BAA2B;IAC3B,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B,4CAA4C;IAC5C,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,yBAAyB;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wBAAwB;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gDAAgD;IAChD,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,oBAAoB;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,0DAA0D;IAC1D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,qCAAqC;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0EAA0E;IAC1E,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;CAC7B;AAED,QAAA,MAAM,OAAO,GAAI,kHAYd,YAAY,4CAwLd,CAAC;AAEF,eAAe,OAAO,CAAC"}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { CategoryScale, Chart as ChartJS, Filler, Legend, LinearScale, LineElement, PointElement, Tooltip, } from 'chart.js';
|
|
4
|
+
import dayjs from 'dayjs';
|
|
5
|
+
import { useMemo } from 'react';
|
|
6
|
+
import { Line } from 'react-chartjs-2';
|
|
7
|
+
import cn from '../../utils/cn.js';
|
|
8
|
+
import { formatAbbreviatedNumber, formatNumberWithComma, } from '../../utils/utils.js';
|
|
9
|
+
import LDSpinner from '../LDSpinner/LDSpinner.js';
|
|
10
|
+
import ChartBase from './ChartBase.js';
|
|
11
|
+
// 이 컴포넌트에서만 그리는 선(라인) 차트라 BarElement는 등록하지 않는다.
|
|
12
|
+
ChartJS.register(CategoryScale, LinearScale, PointElement, LineElement, Filler, Tooltip, Legend);
|
|
13
|
+
const EMPTY_LABEL_FORMATS = {
|
|
14
|
+
day: 'YYYY-MM-DD',
|
|
15
|
+
month: 'YYYY-MM',
|
|
16
|
+
year: 'YYYY',
|
|
17
|
+
};
|
|
18
|
+
// canvas의 strokeStyle/fillStyle은 CSS 변수를 받지 못하므로 토큰 값을 hex로 옮긴다.
|
|
19
|
+
const CHART_COLORS = {
|
|
20
|
+
lowordBlue: '#6891f8', // --color-lb500
|
|
21
|
+
blue: '#2158fd', // --color-blue400
|
|
22
|
+
red: '#ff4747', // --color-red500
|
|
23
|
+
orange: '#ff6a00', // --color-orange500
|
|
24
|
+
yellow: '#ffcc24', // --color-yellow600
|
|
25
|
+
green: '#00de47', // --color-green600
|
|
26
|
+
pink: '#ff33d3', // --color-pink500
|
|
27
|
+
purple: '#7b40c7', // --color-purple500
|
|
28
|
+
};
|
|
29
|
+
const TOOLTIP_TITLE_COLOR = '#e3e6ea'; // --color-neutral100
|
|
30
|
+
// 수직선에 얹는 알파. CHART_COLORS의 hex(#rrggbb)에 이어붙여 #rrggbbaa로 쓴다.
|
|
31
|
+
const VERTICAL_LINE_ALPHA = '80'; // 50%
|
|
32
|
+
// 활성(호버) 데이터 포인트 위치에 수직선을 그리는 플러그인. 앱 전역이 아닌 이 인스턴스에만 적용한다.
|
|
33
|
+
const intersectDataVerticalLine = {
|
|
34
|
+
id: 'intersectDataVerticalLine',
|
|
35
|
+
beforeDraw: (chart) => {
|
|
36
|
+
const [activePoint] = chart.getActiveElements();
|
|
37
|
+
if (!activePoint) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const chartArea = chart.chartArea;
|
|
41
|
+
const ctx = chart.ctx;
|
|
42
|
+
if (chart.data.datasets.length === 1) {
|
|
43
|
+
ctx.beginPath();
|
|
44
|
+
ctx.moveTo(activePoint.element.x, activePoint.element.y);
|
|
45
|
+
ctx.lineTo(activePoint.element.x, chartArea.bottom);
|
|
46
|
+
ctx.lineWidth = 3;
|
|
47
|
+
ctx.strokeStyle = `${chart.data.datasets[0]?.borderColor}${VERTICAL_LINE_ALPHA}`;
|
|
48
|
+
ctx.stroke();
|
|
49
|
+
ctx.restore();
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
const LDChart = ({ dataList, color = 'lowordBlue', position = 'right', reverseYAxis, yUnit, tooltipUnit, unit = 'day', loading, rootClassName, className, style, }) => {
|
|
54
|
+
const emptyLabels = useMemo(() => Array.from({ length: 7 }, (_, i) => dayjs()
|
|
55
|
+
.subtract(6 - i, unit)
|
|
56
|
+
.format(EMPTY_LABEL_FORMATS[unit])), [unit]);
|
|
57
|
+
const maxData = useMemo(() => {
|
|
58
|
+
const values = dataList.value;
|
|
59
|
+
const max = values.length > 0 ? Math.max(...values) : 0;
|
|
60
|
+
// 데이터가 없거나 전부 0이면 max가 0이 되어 축이 무너지므로 기본 축(0~100)을 쓴다.
|
|
61
|
+
if (max <= 0) {
|
|
62
|
+
return 100;
|
|
63
|
+
}
|
|
64
|
+
return Number((max + Math.ceil(max * 0.1)).toFixed(2));
|
|
65
|
+
}, [dataList.value]);
|
|
66
|
+
const dataSets = useMemo(() => {
|
|
67
|
+
const lineColor = CHART_COLORS[color];
|
|
68
|
+
return [
|
|
69
|
+
{
|
|
70
|
+
label: dataList.keyword ?? '',
|
|
71
|
+
data: dataList.value,
|
|
72
|
+
fill: reverseYAxis ? 'start' : 'origin',
|
|
73
|
+
// 위 → 아래로 시리즈 색이 투명해지는 알파 램프(80 → 33 → 00).
|
|
74
|
+
backgroundColor: (context) => {
|
|
75
|
+
const stops = [
|
|
76
|
+
lineColor + '80',
|
|
77
|
+
lineColor + '33',
|
|
78
|
+
lineColor + '00',
|
|
79
|
+
];
|
|
80
|
+
const { chartArea, ctx } = context.chart;
|
|
81
|
+
if (!chartArea) {
|
|
82
|
+
return undefined;
|
|
83
|
+
}
|
|
84
|
+
const gradient = ctx.createLinearGradient(0, chartArea.top, 0, chartArea.bottom);
|
|
85
|
+
gradient.addColorStop(0, stops[0]);
|
|
86
|
+
gradient.addColorStop(0.58, stops[1]);
|
|
87
|
+
gradient.addColorStop(1, stops[2]);
|
|
88
|
+
return gradient;
|
|
89
|
+
},
|
|
90
|
+
hoverBackgroundColor: lineColor,
|
|
91
|
+
borderColor: lineColor,
|
|
92
|
+
pointRadius: 1,
|
|
93
|
+
cubicInterpolationMode: 'monotone',
|
|
94
|
+
pointBackgroundColor: lineColor,
|
|
95
|
+
pointBorderColor: lineColor,
|
|
96
|
+
pointHoverBackgroundColor: lineColor,
|
|
97
|
+
pointHoverBorderColor: lineColor,
|
|
98
|
+
pointHoverBorderWidth: 4,
|
|
99
|
+
pointHoverRadius: 4,
|
|
100
|
+
},
|
|
101
|
+
];
|
|
102
|
+
}, [dataList.keyword, dataList.value, reverseYAxis, color]);
|
|
103
|
+
const options = {
|
|
104
|
+
maintainAspectRatio: false,
|
|
105
|
+
responsive: true,
|
|
106
|
+
scales: {
|
|
107
|
+
y: {
|
|
108
|
+
min: 0,
|
|
109
|
+
max: maxData,
|
|
110
|
+
position,
|
|
111
|
+
ticks: {
|
|
112
|
+
stepSize: Math.ceil(maxData / 10),
|
|
113
|
+
precision: 0,
|
|
114
|
+
callback: (value) => `${formatAbbreviatedNumber(Number(value))}${yUnit ?? ''}`,
|
|
115
|
+
},
|
|
116
|
+
reverse: reverseYAxis || false,
|
|
117
|
+
},
|
|
118
|
+
x: {
|
|
119
|
+
grid: {
|
|
120
|
+
drawOnChartArea: false,
|
|
121
|
+
drawTicks: true,
|
|
122
|
+
},
|
|
123
|
+
ticks: {
|
|
124
|
+
maxRotation: 0,
|
|
125
|
+
minRotation: 0,
|
|
126
|
+
autoSkip: true,
|
|
127
|
+
maxTicksLimit: 12,
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
plugins: {
|
|
132
|
+
tooltip: {
|
|
133
|
+
mode: 'index',
|
|
134
|
+
intersect: false,
|
|
135
|
+
enabled: true,
|
|
136
|
+
titleColor: TOOLTIP_TITLE_COLOR,
|
|
137
|
+
displayColors: true,
|
|
138
|
+
usePointStyle: true,
|
|
139
|
+
borderWidth: 0.2,
|
|
140
|
+
bodySpacing: 8,
|
|
141
|
+
caretPadding: 10,
|
|
142
|
+
callbacks: {
|
|
143
|
+
label: (context) => {
|
|
144
|
+
let label = ' ' + (context.dataset.label || '');
|
|
145
|
+
if (label) {
|
|
146
|
+
label += ': ';
|
|
147
|
+
}
|
|
148
|
+
if (context.parsed.y !== null) {
|
|
149
|
+
label +=
|
|
150
|
+
formatNumberWithComma(context.parsed.y) + (tooltipUnit ?? '');
|
|
151
|
+
}
|
|
152
|
+
return label;
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
legend: {
|
|
157
|
+
display: false,
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
interaction: {
|
|
161
|
+
mode: 'nearest',
|
|
162
|
+
intersect: false,
|
|
163
|
+
axis: 'x',
|
|
164
|
+
},
|
|
165
|
+
};
|
|
166
|
+
// sizing은 차트 영역(Viewport)으로, 그 외 스타일은 Root로 분리해 전달한다.
|
|
167
|
+
const { width, height, minWidth, maxWidth, minHeight, maxHeight, ...rootStyle } = style ?? {};
|
|
168
|
+
const viewportStyle = {
|
|
169
|
+
height: height ?? 300,
|
|
170
|
+
width: width ?? '100%',
|
|
171
|
+
minWidth,
|
|
172
|
+
maxWidth,
|
|
173
|
+
minHeight,
|
|
174
|
+
maxHeight,
|
|
175
|
+
};
|
|
176
|
+
// 배경은 기본 투명. 필요한 소비처가 rootClassName으로 지정한다.
|
|
177
|
+
// (원본의 bg-mono5는 정의되지 않은 토큰이라 실제로도 투명하게 렌더됐다.)
|
|
178
|
+
return (_jsx(ChartBase.Root, { className: cn('w-full flex flex-col items-start justify-start rounded-xs', rootClassName), style: rootStyle, children: _jsxs(ChartBase.Viewport, { style: viewportStyle, className: cn('relative', className), children: [_jsx(Line, { height: Number(viewportStyle.height), data: {
|
|
179
|
+
labels: dataList.label.length > 0 ? dataList.label : emptyLabels,
|
|
180
|
+
datasets: dataSets,
|
|
181
|
+
}, options: options, plugins: [intersectDataVerticalLine] }), _jsx(ChartBase.Overlay, { className: cn('absolute inset-0 z-10 flex items-center justify-center', 'bg-white/70 ld-transition', loading ? 'opacity-100' : 'opacity-0 pointer-events-none'), children: _jsx(LDSpinner, { size: 32, className: "text-lb500" }) })] }) }));
|
|
182
|
+
};
|
|
183
|
+
export default LDChart;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { LDButtonProps } from '../LDButton/LDButton.js';
|
|
2
|
+
/** 우편번호 검색 결과 중 소비처로 넘기는 값만 추린 주소 */
|
|
3
|
+
export interface LDDaumPostAddress {
|
|
4
|
+
/** 5자리 우편번호 */
|
|
5
|
+
zonecode: string;
|
|
6
|
+
/** 도로명 주소(국문) */
|
|
7
|
+
address: string;
|
|
8
|
+
/** 도로명 주소(영문) */
|
|
9
|
+
addressEnglish: string;
|
|
10
|
+
/** 시군구 영문명 — 없으면 시도 영문명으로 대체 */
|
|
11
|
+
sigunguEnglish: string;
|
|
12
|
+
}
|
|
13
|
+
export interface LDDaumPostButtonProps extends LDButtonProps<'button'> {
|
|
14
|
+
onComplete: (address: LDDaumPostAddress) => void;
|
|
15
|
+
}
|
|
16
|
+
declare const LDDaumPostButton: ({ onComplete, size, children, variant, color, disabled, className, ...props }: LDDaumPostButtonProps) => import("react/jsx-runtime").JSX.Element;
|
|
17
|
+
export default LDDaumPostButton;
|
|
18
|
+
//# sourceMappingURL=LDDaumPostButton.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LDDaumPostButton.d.ts","sourceRoot":"","sources":["../../../src/components/LDDaumPostButton/LDDaumPostButton.tsx"],"names":[],"mappings":"AAGA,OAAiB,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAO/D,qCAAqC;AACrC,MAAM,WAAW,iBAAiB;IAChC,eAAe;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,iBAAiB;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,gCAAgC;IAChC,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,qBAAsB,SAAQ,aAAa,CAAC,QAAQ,CAAC;IACpE,UAAU,EAAE,CAAC,OAAO,EAAE,iBAAiB,KAAK,IAAI,CAAC;CAClD;AAED,QAAA,MAAM,gBAAgB,GAAI,+EASvB,qBAAqB,4CA2CvB,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useKakaoPostcodePopup } from 'react-daum-postcode';
|
|
4
|
+
import LDButton from '../LDButton/LDButton.js';
|
|
5
|
+
import LDToast from '../LDToast/LDToast.js';
|
|
6
|
+
// 우편번호 팝업 창 크기 — 카카오(다음) 우편번호 서비스 기본값. 중앙 정렬 계산에 같이 쓴다.
|
|
7
|
+
const POPUP_WIDTH = 500;
|
|
8
|
+
const POPUP_HEIGHT = 600;
|
|
9
|
+
const LDDaumPostButton = ({ onComplete, size = 'small', children = '우편번호 검색', variant = 'outline', color = 'neutral', disabled, className, ...props }) => {
|
|
10
|
+
const open = useKakaoPostcodePopup();
|
|
11
|
+
const handleComplete = (data) => {
|
|
12
|
+
if (!data.roadAddress) {
|
|
13
|
+
LDToast.warning('도로명 주소가 없는 주소입니다.');
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
onComplete({
|
|
17
|
+
zonecode: data.zonecode,
|
|
18
|
+
address: data.roadAddress,
|
|
19
|
+
addressEnglish: data.roadAddressEnglish,
|
|
20
|
+
sigunguEnglish: data.sigunguEnglish || data.sidoEnglish,
|
|
21
|
+
});
|
|
22
|
+
};
|
|
23
|
+
return (_jsx(LDButton, { type: "button", size: size, variant: variant, color: color, disabled: disabled, className: className, onClick: () => open({
|
|
24
|
+
width: POPUP_WIDTH,
|
|
25
|
+
height: POPUP_HEIGHT,
|
|
26
|
+
left: Math.ceil((window.screen.width - POPUP_WIDTH) / 2),
|
|
27
|
+
top: Math.ceil((window.screen.height - POPUP_HEIGHT) / 2),
|
|
28
|
+
onComplete: handleComplete,
|
|
29
|
+
onError: () => LDToast.error('우편번호 서비스를 불러올 수 없습니다. 잠시 후 다시 시도해 주세요.'),
|
|
30
|
+
}), ...props, children: children }));
|
|
31
|
+
};
|
|
32
|
+
export default LDDaumPostButton;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type ComponentProps, type ReactNode } from 'react';
|
|
2
|
+
import SegmentedBase from './SegmentedBase.js';
|
|
3
|
+
/** 세그먼트 크기 */
|
|
4
|
+
export type LDSegmentedSize = 'small' | 'medium' | 'large';
|
|
5
|
+
/** 활성 세그먼트 강조 색 */
|
|
6
|
+
export type LDSegmentedColor = 'point' | 'danger' | 'success' | 'warning';
|
|
7
|
+
/** LDSegmented.Root props */
|
|
8
|
+
export interface LDSegmentedRootProps extends ComponentProps<typeof SegmentedBase.Root> {
|
|
9
|
+
/** 언컨트롤드일 때 초기 선택값 */
|
|
10
|
+
defaultValue?: string;
|
|
11
|
+
/** 세그먼트 크기. 기본 'medium' */
|
|
12
|
+
size?: LDSegmentedSize;
|
|
13
|
+
/** true면 트랙·세그먼트 모두 pill 형태(rounded-full). 기본 false */
|
|
14
|
+
rounded?: boolean;
|
|
15
|
+
}
|
|
16
|
+
interface LDSegmentedItemOwnProps {
|
|
17
|
+
/** children 왼쪽에 렌더되는 아이콘. 크기는 size에 따라 자동 결정됩니다. */
|
|
18
|
+
icon?: ReactNode;
|
|
19
|
+
/** 활성 세그먼트 강조 색. 기본 'point' */
|
|
20
|
+
color?: LDSegmentedColor;
|
|
21
|
+
}
|
|
22
|
+
/** LDSegmented.Item props */
|
|
23
|
+
export type LDSegmentedItemProps = Omit<ComponentProps<typeof SegmentedBase.Item>, 'ref'> & LDSegmentedItemOwnProps;
|
|
24
|
+
declare const LDSegmented: {
|
|
25
|
+
Root: ({ size, rounded, value, defaultValue, onValueChange, disabled, className, style, children, ...props }: LDSegmentedRootProps) => import("react/jsx-runtime").JSX.Element;
|
|
26
|
+
Item: ({ value, className, disabled, icon, color, children, ...props }: LDSegmentedItemProps) => import("react/jsx-runtime").JSX.Element;
|
|
27
|
+
};
|
|
28
|
+
export default LDSegmented;
|
|
29
|
+
//# sourceMappingURL=LDSegmented.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LDSegmented.d.ts","sourceRoot":"","sources":["../../../src/components/LDSegmented/LDSegmented.tsx"],"names":[],"mappings":"AAEA,OAAO,EAOL,KAAK,cAAc,EACnB,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAGf,OAAO,aAAa,MAAM,iBAAiB,CAAC;AAK5C,cAAc;AACd,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC;AAC3D,mBAAmB;AACnB,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,CAAC;AA8B1E,6BAA6B;AAC7B,MAAM,WAAW,oBAAqB,SAAQ,cAAc,CAC1D,OAAO,aAAa,CAAC,IAAI,CAC1B;IACC,sBAAsB;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,2BAA2B;IAC3B,IAAI,CAAC,EAAE,eAAe,CAAC;IACvB,uDAAuD;IACvD,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AA4JD,UAAU,uBAAuB;IAC/B,oDAAoD;IACpD,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,+BAA+B;IAC/B,KAAK,CAAC,EAAE,gBAAgB,CAAC;CAC1B;AAED,6BAA6B;AAC7B,MAAM,MAAM,oBAAoB,GAAG,IAAI,CACrC,cAAc,CAAC,OAAO,aAAa,CAAC,IAAI,CAAC,EACzC,KAAK,CACN,GACC,uBAAuB,CAAC;AA0D1B,QAAA,MAAM,WAAW;kHA5Md,oBAAoB;4EA6JpB,oBAAoB;CAkDtB,CAAC;AAEF,eAAe,WAAW,CAAC"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { createContext, useCallback, useContext, useEffect, useRef, useState, } from 'react';
|
|
4
|
+
import cn from '../../utils/cn.js';
|
|
5
|
+
import { renderIcon } from '../../utils/renderIcon.js';
|
|
6
|
+
import SegmentedBase from './SegmentedBase.js';
|
|
7
|
+
import { useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect.js';
|
|
8
|
+
const LDSegmentedContext = createContext({
|
|
9
|
+
size: 'medium',
|
|
10
|
+
rounded: false,
|
|
11
|
+
measured: false,
|
|
12
|
+
});
|
|
13
|
+
const useLDSegmentedContext = () => useContext(LDSegmentedContext);
|
|
14
|
+
// ── Root ──
|
|
15
|
+
const trackRadiusClass = {
|
|
16
|
+
small: 'rounded-[8px]',
|
|
17
|
+
medium: 'rounded-[10px]',
|
|
18
|
+
large: 'rounded-[12px]',
|
|
19
|
+
};
|
|
20
|
+
// indicator 위치 계산도 같은 값을 써야 트랙 padding과 어긋나지 않으므로 한 곳에서만 정의한다.
|
|
21
|
+
const trackPadding = {
|
|
22
|
+
small: '2px',
|
|
23
|
+
medium: '3px',
|
|
24
|
+
large: '4px',
|
|
25
|
+
};
|
|
26
|
+
// 활성 표시는 Root의 마지막 자식인 indicator 상자가 좌우로 슬라이드하며 담당한다.
|
|
27
|
+
// Item 너비가 가변이라 calc()로 계산할 수 없어 실제 DOM 위치·크기를 측정해 인라인 style로 넣는다.
|
|
28
|
+
const Root = ({ size = 'medium', rounded = false, value, defaultValue, onValueChange, disabled, className, style, children, ...props }) => {
|
|
29
|
+
const [internalValue, setInternalValue] = useState(defaultValue);
|
|
30
|
+
const isControlled = value !== undefined;
|
|
31
|
+
const currentValue = isControlled ? value : internalValue;
|
|
32
|
+
const pad = trackPadding[size];
|
|
33
|
+
const rootRef = useRef(null);
|
|
34
|
+
const [rect, setRect] = useState(null);
|
|
35
|
+
// Base가 등록/해제 시점마다 DOM 순서로 정렬해 알려주는 Item 목록
|
|
36
|
+
const [items, setItems] = useState([]);
|
|
37
|
+
// 활성 Item은 items에서 currentValue로 조회
|
|
38
|
+
const measure = useCallback(() => {
|
|
39
|
+
if (!currentValue) {
|
|
40
|
+
setRect(null);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const activeEl = items.find((item) => item.value === currentValue)?.el;
|
|
44
|
+
if (!activeEl) {
|
|
45
|
+
setRect(null);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const next = {
|
|
49
|
+
left: activeEl.offsetLeft,
|
|
50
|
+
top: activeEl.offsetTop,
|
|
51
|
+
width: activeEl.offsetWidth,
|
|
52
|
+
height: activeEl.offsetHeight,
|
|
53
|
+
};
|
|
54
|
+
setRect((prev) => prev &&
|
|
55
|
+
Object.keys(next).every((key) => prev[key] === next[key])
|
|
56
|
+
? prev
|
|
57
|
+
: next);
|
|
58
|
+
}, [currentValue, items]);
|
|
59
|
+
useIsomorphicLayoutEffect(() => {
|
|
60
|
+
measure();
|
|
61
|
+
}, [currentValue, items, size, rounded, measure]);
|
|
62
|
+
// 비활성 Item의 폭 변화도 활성 Item의 offsetLeft를 밀 수 있으므로 items의 모든 Item을 관찰한다.
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
const root = rootRef.current;
|
|
65
|
+
if (!root)
|
|
66
|
+
return;
|
|
67
|
+
const observer = new ResizeObserver(measure);
|
|
68
|
+
observer.observe(root);
|
|
69
|
+
items.forEach(({ el }) => observer.observe(el));
|
|
70
|
+
return () => observer.disconnect();
|
|
71
|
+
}, [currentValue, items, size, rounded, measure]);
|
|
72
|
+
return (_jsx(LDSegmentedContext, { value: { size, rounded, measured: rect !== null }, children: _jsxs(SegmentedBase.Root, { ref: rootRef, value: currentValue, onValueChange: (newValue) => {
|
|
73
|
+
if (!isControlled)
|
|
74
|
+
setInternalValue(newValue);
|
|
75
|
+
onValueChange?.(newValue);
|
|
76
|
+
}, onItemsChange: setItems, disabled: disabled, className: cn('inline-flex items-stretch relative isolate bg-mono100', rounded ? 'rounded-full' : trackRadiusClass[size], disabled && 'opacity-60', className), style: { padding: pad, ...style }, ...props, children: [children, rect && (_jsx(SegmentedBase.Indicator, { className: cn('absolute -z-10 pointer-events-none ld-transition bg-white shadow-sm', rounded ? 'rounded-full' : itemRadiusClass[size]), style: {
|
|
77
|
+
left: 0,
|
|
78
|
+
top: 0,
|
|
79
|
+
width: `${rect.width}px`,
|
|
80
|
+
height: `${rect.height}px`,
|
|
81
|
+
transform: `translate(${rect.left}px, ${rect.top}px)`,
|
|
82
|
+
} }))] }) }));
|
|
83
|
+
};
|
|
84
|
+
// ── Item ──
|
|
85
|
+
const itemRadiusClass = {
|
|
86
|
+
small: 'rounded-[6px]',
|
|
87
|
+
medium: 'rounded-[7px]',
|
|
88
|
+
large: 'rounded-[8px]',
|
|
89
|
+
};
|
|
90
|
+
const itemPaddingClass = {
|
|
91
|
+
small: 'px-2 py-1',
|
|
92
|
+
medium: 'px-3 py-1.5',
|
|
93
|
+
large: 'px-4 py-2',
|
|
94
|
+
};
|
|
95
|
+
const itemFontClass = {
|
|
96
|
+
small: 'display12',
|
|
97
|
+
medium: 'display14',
|
|
98
|
+
large: 'display16',
|
|
99
|
+
};
|
|
100
|
+
const itemIconClass = {
|
|
101
|
+
small: 'icon-sm',
|
|
102
|
+
medium: 'icon-md',
|
|
103
|
+
large: 'icon-lg',
|
|
104
|
+
};
|
|
105
|
+
/** 활성 세그먼트 텍스트 색 */
|
|
106
|
+
const colorTextClass = {
|
|
107
|
+
point: 'data-[state=on]:text-lb600',
|
|
108
|
+
danger: 'data-[state=on]:text-danger600',
|
|
109
|
+
success: 'data-[state=on]:text-success800',
|
|
110
|
+
warning: 'data-[state=on]:text-warning800',
|
|
111
|
+
};
|
|
112
|
+
// 활성 배경은 Root의 indicator가 전담하므로 Item 자체는 투명하다.
|
|
113
|
+
const Item = ({ value, className, disabled, icon, color = 'point', children, ...props }) => {
|
|
114
|
+
const { size, rounded, measured } = useLDSegmentedContext();
|
|
115
|
+
return (_jsxs(SegmentedBase.Item, { value: value, disabled: disabled, className: cn('group relative flex items-center justify-center gap-xs ld-transition cursor-pointer text-nowrap', rounded ? 'rounded-full' : itemRadiusClass[size], itemPaddingClass[size], itemFontClass[size], 'data-[state=off]:text-neutral500 data-[state=off]:not-data-disabled:hover:text-neutral700', colorTextClass[color], 'data-disabled:opacity-40 data-disabled:cursor-not-allowed', className), ...props, children: [!measured && (_jsx("span", { "aria-hidden": true, className: cn('absolute inset-0 -z-10 pointer-events-none bg-white shadow-sm opacity-0 group-data-[state=on]:opacity-100', rounded ? 'rounded-full' : itemRadiusClass[size]) })), renderIcon(icon, itemIconClass[size]), _jsxs("span", { className: "relative text-nowrap", children: [_jsx("span", { "aria-hidden": true, className: "absolute inset-0 text-nowrap group-data-[state=on]:opacity-0", children: children }), _jsx("span", { className: "bold text-nowrap opacity-0 group-data-[state=on]:opacity-100", children: children })] })] }));
|
|
116
|
+
};
|
|
117
|
+
const LDSegmented = {
|
|
118
|
+
Root,
|
|
119
|
+
Item,
|
|
120
|
+
};
|
|
121
|
+
export default LDSegmented;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type CSSProperties, type ReactNode, type Ref } from 'react';
|
|
2
|
+
interface RootProps {
|
|
3
|
+
ref?: Ref<HTMLDivElement>;
|
|
4
|
+
value?: string;
|
|
5
|
+
onValueChange?: (value: string) => void;
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
className?: string;
|
|
8
|
+
style?: CSSProperties;
|
|
9
|
+
children?: ReactNode;
|
|
10
|
+
/** 등록된 Item 목록이 바뀔 때 DOM 순서로 정렬해 알려준다. Custom 레이어의 indicator 실측용. */
|
|
11
|
+
onItemsChange?: (items: {
|
|
12
|
+
value: string;
|
|
13
|
+
el: HTMLElement;
|
|
14
|
+
}[]) => void;
|
|
15
|
+
}
|
|
16
|
+
interface ItemProps {
|
|
17
|
+
value: string;
|
|
18
|
+
disabled?: boolean;
|
|
19
|
+
className?: string;
|
|
20
|
+
style?: CSSProperties;
|
|
21
|
+
children?: ReactNode;
|
|
22
|
+
}
|
|
23
|
+
interface IndicatorProps {
|
|
24
|
+
className?: string;
|
|
25
|
+
style?: CSSProperties;
|
|
26
|
+
}
|
|
27
|
+
export declare const SegmentedBase: {
|
|
28
|
+
Root: ({ ref, value, onValueChange, disabled, className, style, children, onItemsChange, ...props }: RootProps) => import("react/jsx-runtime").JSX.Element;
|
|
29
|
+
Item: ({ value, disabled, className, style, children }: ItemProps) => import("react/jsx-runtime").JSX.Element;
|
|
30
|
+
Indicator: ({ className, style }: IndicatorProps) => import("react/jsx-runtime").JSX.Element;
|
|
31
|
+
};
|
|
32
|
+
export default SegmentedBase;
|
|
33
|
+
//# sourceMappingURL=SegmentedBase.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SegmentedBase.d.ts","sourceRoot":"","sources":["../../../src/components/LDSegmented/SegmentedBase.tsx"],"names":[],"mappings":"AAEA,OAAO,EAOL,KAAK,aAAa,EAElB,KAAK,SAAS,EACd,KAAK,GAAG,EACT,MAAM,OAAO,CAAC;AAWf,UAAU,SAAS;IACjB,GAAG,CAAC,EAAE,GAAG,CAAC,cAAc,CAAC,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,qEAAqE;IACrE,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,WAAW,CAAA;KAAE,EAAE,KAAK,IAAI,CAAC;CACvE;AA+ID,UAAU,SAAS;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAyED,UAAU,cAAc;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,aAAa,CAAC;CACvB;AAMD,eAAO,MAAM,aAAa;yGAxMvB,SAAS;4DAwHmD,SAAS;sCA4E/B,cAAc;CAQtD,CAAC;AAEF,eAAe,aAAa,CAAC"}
|