sellmate-design-system-react 9.0.0-beta.48 → 9.0.0-beta.49

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.js CHANGED
@@ -4917,11 +4917,19 @@ var SChatFileImpl = /* @__PURE__ */ forwardRef(function SChatFile({ name, extens
4917
4917
  });
4918
4918
  var SChatFile2 = /* @__PURE__ */ withDisplayName(SChatFileImpl, "SChatFile");
4919
4919
  var CHAT_MESSAGE_DIRECTIONS = ["incoming", "outgoing"];
4920
- var CHAT_MESSAGE_VARIANTS = ["default", "template"];
4920
+ var CHAT_MESSAGE_VARIANTS = ["default", "template", "admin"];
4921
4921
  var BUBBLE_SURFACE = {
4922
4922
  incoming: "bg-(--cmp-chatBubble-incoming-bg) text-(--cmp-chatBubble-incoming-content)",
4923
- outgoing: "bg-(--cmp-chatBubble-outgoing-bg) text-(--cmp-chatBubble-outgoing-content)",
4924
- template: "bg-(--cmp-chatBubble-templete-bg) text-(--cmp-chatBubble-templete-content)"
4923
+ outgoing: "bg-(--cmp-chatBubble-outgoing-general-bg) text-(--cmp-chatBubble-outgoing-general-content)",
4924
+ template: "bg-(--cmp-chatBubble-templete-bg) text-(--cmp-chatBubble-templete-content)",
4925
+ // TODO: `chatBubble.outgoing.admin` 토큰이 생기면 둘 다 그쪽으로 교체한다.
4926
+ // 지금은 admin 면을 가리키는 컴포넌트 토큰이 없어 글자색만 시맨틱 토큰을 직접 쓴다.
4927
+ admin: "bg-(--cmp-chatBubble-outgoing-default-bg) text-(--sys-color-fg-deep)"
4928
+ };
4929
+ var OUTGOING_SURFACE = {
4930
+ default: "outgoing",
4931
+ template: "template",
4932
+ admin: "admin"
4925
4933
  };
4926
4934
  var CHAT_MESSAGE_READ_STATUSES = ["read", "unread"];
4927
4935
  var READ_STATUS_LABEL = {
@@ -4976,7 +4984,7 @@ var SChatMessageImpl = /* @__PURE__ */ forwardRef(function SChatMessage({
4976
4984
  ...rest
4977
4985
  }, ref) {
4978
4986
  const outgoing = direction === "outgoing";
4979
- const surface = direction === "outgoing" && variant === "template" ? "template" : direction;
4987
+ const surface = outgoing ? OUTGOING_SURFACE[variant] : "incoming";
4980
4988
  const bubbles = messages ?? [];
4981
4989
  const files = attachments ?? [];
4982
4990
  return /* @__PURE__ */ jsxs(
@@ -17849,6 +17857,673 @@ function SKeyValueTable({
17849
17857
  )
17850
17858
  ] });
17851
17859
  }
17860
+
17861
+ // src/lib/chart/scale.ts
17862
+ function niceNum(range, round) {
17863
+ if (range <= 0) return 1;
17864
+ const exponent = Math.floor(Math.log10(range));
17865
+ const fraction = range / 10 ** exponent;
17866
+ let nice;
17867
+ if (round) {
17868
+ nice = fraction < 1.5 ? 1 : fraction < 3 ? 2 : fraction < 7 ? 5 : 10;
17869
+ } else {
17870
+ nice = fraction <= 1 ? 1 : fraction <= 2 ? 2 : fraction <= 5 ? 5 : 10;
17871
+ }
17872
+ return nice * 10 ** exponent;
17873
+ }
17874
+ function trim(value, step) {
17875
+ const decimals = Math.max(0, -Math.floor(Math.log10(step)));
17876
+ return Number(value.toFixed(Math.min(decimals, 10)));
17877
+ }
17878
+ function niceScale(values, tickCount, fixedMax) {
17879
+ const finite = values.filter(Number.isFinite);
17880
+ const count = Math.max(2, Math.floor(tickCount));
17881
+ const rawMin = Math.min(0, ...finite);
17882
+ const rawMax = fixedMax ?? Math.max(0, ...finite);
17883
+ if (!Number.isFinite(rawMin) || !Number.isFinite(rawMax) || rawMax === rawMin) {
17884
+ const ticks2 = Array.from({ length: count }, (_, i) => i);
17885
+ return { min: 0, max: count - 1, ticks: ticks2 };
17886
+ }
17887
+ const range = niceNum(rawMax - rawMin, false);
17888
+ const step = niceNum(range / (count - 1), true);
17889
+ const min = trim(Math.floor(rawMin / step) * step, step);
17890
+ const max = trim(Math.ceil(rawMax / step) * step, step);
17891
+ const ticks = [];
17892
+ for (let v = min; v <= max + step / 2; v += step) ticks.push(trim(v, step));
17893
+ return { min, max, ticks };
17894
+ }
17895
+ function ratioOf(value, scale) {
17896
+ const span = scale.max - scale.min;
17897
+ if (span <= 0) return 0;
17898
+ return (value - scale.min) / span;
17899
+ }
17900
+ var AXIS_LABEL_GAP = 8;
17901
+ function ChartGrid({ scale, direction, width, height }) {
17902
+ const vertical = direction === "vertical";
17903
+ return /* @__PURE__ */ jsx("g", { "aria-hidden": true, children: scale.ticks.map((tick) => {
17904
+ const ratio = ratioOf(tick, scale);
17905
+ const isBaseline = tick === 0;
17906
+ const color = isBaseline ? "var(--cmp-chart-baseline-color)" : "var(--cmp-chart-grid-line-color)";
17907
+ const position = vertical ? height - ratio * height : ratio * width;
17908
+ const extent = vertical ? height : width;
17909
+ const snapped = Math.min(Math.max(Math.round(position), 0), Math.max(0, extent - 1)) + 0.5;
17910
+ return vertical ? /* @__PURE__ */ jsx(
17911
+ "line",
17912
+ {
17913
+ x1: 0,
17914
+ x2: width,
17915
+ y1: snapped,
17916
+ y2: snapped,
17917
+ stroke: color,
17918
+ strokeWidth: 1
17919
+ },
17920
+ tick
17921
+ ) : /* @__PURE__ */ jsx(
17922
+ "line",
17923
+ {
17924
+ x1: snapped,
17925
+ x2: snapped,
17926
+ y1: 0,
17927
+ y2: height,
17928
+ stroke: color,
17929
+ strokeWidth: 1
17930
+ },
17931
+ tick
17932
+ );
17933
+ }) });
17934
+ }
17935
+ function ChartAxisLabels({
17936
+ scale,
17937
+ direction,
17938
+ format,
17939
+ className,
17940
+ style,
17941
+ sampleRef
17942
+ }) {
17943
+ const vertical = direction === "vertical";
17944
+ const widest = scale.ticks.reduce((longest, tick) => {
17945
+ const text = format(tick);
17946
+ return text.length > longest.length ? text : longest;
17947
+ }, "");
17948
+ return /* @__PURE__ */ jsxs(
17949
+ "div",
17950
+ {
17951
+ "aria-hidden": true,
17952
+ className: cn(
17953
+ "relative typo-body-sm-medium text-(--cmp-chart-axis-color)",
17954
+ vertical ? "h-full" : "w-full",
17955
+ className
17956
+ ),
17957
+ style,
17958
+ children: [
17959
+ scale.ticks.map((tick) => {
17960
+ const ratio = ratioOf(tick, scale);
17961
+ return vertical ? /* @__PURE__ */ jsx(
17962
+ "span",
17963
+ {
17964
+ className: "absolute right-0 -translate-y-1/2 whitespace-nowrap",
17965
+ style: { top: `${(1 - ratio) * 100}%` },
17966
+ children: format(tick)
17967
+ },
17968
+ tick
17969
+ ) : /* @__PURE__ */ jsx(
17970
+ "span",
17971
+ {
17972
+ className: "absolute top-0 -translate-x-1/2 whitespace-nowrap",
17973
+ style: { left: `${ratio * 100}%` },
17974
+ children: format(tick)
17975
+ },
17976
+ tick
17977
+ );
17978
+ }),
17979
+ /* @__PURE__ */ jsx("span", { ref: sampleRef, "aria-hidden": true, className: "invisible inline-block whitespace-nowrap", children: vertical ? widest : "0" })
17980
+ ]
17981
+ }
17982
+ );
17983
+ }
17984
+ var ChartFrame = /* @__PURE__ */ forwardRef(function ChartFrame2({ legend, legendPosition = "top", children, className, style, ...props }, ref) {
17985
+ return /* @__PURE__ */ jsxs(
17986
+ "div",
17987
+ {
17988
+ ref,
17989
+ className: cn(
17990
+ "flex w-full flex-col gap-(--cmp-chart-gap)",
17991
+ "p-(--cmp-chart-paddingAll) bg-(--cmp-chart-bg)",
17992
+ className
17993
+ ),
17994
+ style,
17995
+ ...props,
17996
+ children: [
17997
+ legend && legendPosition === "top" && legend,
17998
+ children,
17999
+ legend && legendPosition === "bottom" && legend
18000
+ ]
18001
+ }
18002
+ );
18003
+ });
18004
+ var DOT_SIZE = 6;
18005
+ function ChartLegend({ items, align = "center", activeIndex, className }) {
18006
+ if (items.length === 0) return null;
18007
+ return /* @__PURE__ */ jsx(
18008
+ "ul",
18009
+ {
18010
+ className: cn(
18011
+ "flex list-none flex-wrap items-center gap-x-(--cmp-chart-legend-group-gap) p-0",
18012
+ align === "start" && "justify-start",
18013
+ align === "center" && "justify-center",
18014
+ align === "end" && "justify-end",
18015
+ className
18016
+ ),
18017
+ children: items.map((item4, index) => /* @__PURE__ */ jsxs(
18018
+ "li",
18019
+ {
18020
+ className: cn(
18021
+ "flex items-center gap-(--cmp-chart-legend-legendItem-gap)",
18022
+ "py-(--cmp-chart-legend-legendItem-paddingY)",
18023
+ "typo-body-sm-medium text-(--cmp-chart-legend-legendItem-color)",
18024
+ "transition-opacity",
18025
+ activeIndex != null && activeIndex !== index && "opacity-(--opacity-060)"
18026
+ ),
18027
+ children: [
18028
+ /* @__PURE__ */ jsx(
18029
+ "span",
18030
+ {
18031
+ "aria-hidden": true,
18032
+ className: "shrink-0 rounded-full",
18033
+ style: { width: DOT_SIZE, height: DOT_SIZE, background: item4.color }
18034
+ }
18035
+ ),
18036
+ item4.name
18037
+ ]
18038
+ },
18039
+ `${item4.name}-${index}`
18040
+ ))
18041
+ }
18042
+ );
18043
+ }
18044
+
18045
+ // src/lib/chart/palette.ts
18046
+ var V3 = (path) => `var(--cmp-chart-palette-${path})`;
18047
+ var CHART_COLORS = {
18048
+ positivePrimary: V3("variation-positivePrimary"),
18049
+ positiveSecondary: V3("variation-positiveSecondary"),
18050
+ negativePrimary: V3("variation-negativePrimary"),
18051
+ negativeSecondary: V3("variation-negativeSecondary"),
18052
+ neutralPrimary: V3("variation-neutralPrimary"),
18053
+ neutralSecondary: V3("variation-neutralSecondary")
18054
+ };
18055
+ var CHART_PALETTES = {
18056
+ default: [V3("single")],
18057
+ multi: [
18058
+ V3("multi-blue"),
18059
+ V3("multi-skyblue"),
18060
+ V3("multi-yellow"),
18061
+ V3("multi-olive"),
18062
+ V3("multi-lightRed"),
18063
+ V3("multi-warmBlue"),
18064
+ V3("multi-orange"),
18065
+ V3("multi-green"),
18066
+ V3("multi-navy"),
18067
+ V3("multi-grey"),
18068
+ V3("multi-red")
18069
+ ],
18070
+ gradation: [
18071
+ V3("gradation-1"),
18072
+ V3("gradation-2"),
18073
+ V3("gradation-3"),
18074
+ V3("gradation-4"),
18075
+ V3("gradation-5"),
18076
+ V3("gradation-6")
18077
+ ]
18078
+ };
18079
+ function paletteColor(palette, index, colors) {
18080
+ if (palette === "custom") {
18081
+ if (!colors?.length) return CHART_PALETTES.default[0];
18082
+ return CHART_COLORS[colors[index % colors.length]];
18083
+ }
18084
+ const list = CHART_PALETTES[palette] ?? CHART_PALETTES.default;
18085
+ return list[index % list.length];
18086
+ }
18087
+ function useElementSize() {
18088
+ const ref = useRef(null);
18089
+ const [size, setSize] = useState({ width: 0, height: 0 });
18090
+ useEffect(() => {
18091
+ const element = ref.current;
18092
+ if (!element) return;
18093
+ const update = () => {
18094
+ const { width, height } = element.getBoundingClientRect();
18095
+ setSize(
18096
+ (prev) => Math.abs(prev.width - width) < 0.5 && Math.abs(prev.height - height) < 0.5 ? prev : { width, height }
18097
+ );
18098
+ };
18099
+ update();
18100
+ if (typeof ResizeObserver === "undefined") return;
18101
+ const observer = new ResizeObserver(update);
18102
+ observer.observe(element);
18103
+ return () => observer.disconnect();
18104
+ }, []);
18105
+ return { ref, size };
18106
+ }
18107
+ var DOT_SIZE2 = 6;
18108
+ var CURSOR_OFFSET = 12;
18109
+ function place(cursor, size, extent) {
18110
+ if (!extent || !size) return cursor + CURSOR_OFFSET;
18111
+ const after = cursor + CURSOR_OFFSET;
18112
+ if (after + size <= extent) return after;
18113
+ const before = cursor - CURSOR_OFFSET - size;
18114
+ if (before >= 0) return before;
18115
+ return Math.max(0, Math.min(after, extent - size));
18116
+ }
18117
+ function ChartTooltip({ title, items, x, y, bounds, className }) {
18118
+ const { ref, size } = useElementSize();
18119
+ const measured = size.width > 0 && size.height > 0;
18120
+ return /* @__PURE__ */ jsxs(
18121
+ "div",
18122
+ {
18123
+ ref,
18124
+ role: "tooltip",
18125
+ className: cn(
18126
+ // left/top 대신 transform 으로 옮긴다. 좌표를 바꾸면 마우스가 움직일 때마다
18127
+ // 리플로우가 나 차트가 미세하게 흔들려 보인다. transform 은 레이아웃을
18128
+ // 건드리지 않으므로, 크기를 재기 전에 잠깐 밖으로 나가도 스크롤이 생기지 않는다.
18129
+ "pointer-events-none absolute top-0 left-0 z-10 w-max",
18130
+ "flex flex-col gap-(--cmp-chart-chartTooltip-gap)",
18131
+ "px-(--cmp-chart-chartTooltip-paddingX) py-(--cmp-chart-chartTooltip-paddingY)",
18132
+ "rounded-(--cmp-chart-chartTooltip-radius) bg-(--cmp-chart-chartTooltip-bg) shadow-floating",
18133
+ className
18134
+ ),
18135
+ style: {
18136
+ // 크기를 재기 전에는 원점에 숨겨 둔다. 커서 옆에 그려 두면 그림 밖으로
18137
+ // 넘쳐 스크롤 영역이 잠깐 늘어나고, 차트 전체가 한 번 흔들린다.
18138
+ // transform 도 스크롤 영역 계산에는 포함되므로 위치만 바꿔서는 못 막는다.
18139
+ transform: measured ? `translate(${place(x, size.width, bounds?.width ?? 0)}px, ${place(
18140
+ y,
18141
+ size.height,
18142
+ bounds?.height ?? 0
18143
+ )}px)` : void 0,
18144
+ visibility: measured ? void 0 : "hidden"
18145
+ },
18146
+ children: [
18147
+ /* @__PURE__ */ jsx("span", { className: "typo-body-md-bold text-(--cmp-chart-chartTooltip-title-color)", children: title }),
18148
+ items.map((item4, index) => /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-sd-8", children: [
18149
+ /* @__PURE__ */ jsx(
18150
+ "span",
18151
+ {
18152
+ "aria-hidden": true,
18153
+ className: "shrink-0 rounded-full",
18154
+ style: { width: DOT_SIZE2, height: DOT_SIZE2, background: item4.color }
18155
+ }
18156
+ ),
18157
+ /* @__PURE__ */ jsx("span", { className: "typo-body-sm-default text-(--sys-color-fg-primary)", children: item4.label }),
18158
+ /* @__PURE__ */ jsx("span", { className: "typo-body-sm-bold ml-auto text-(--sys-color-fg-primary)", children: item4.value })
18159
+ ] }, `${item4.label}-${index}`))
18160
+ ]
18161
+ }
18162
+ );
18163
+ }
18164
+
18165
+ // src/components/SBarChart/barChart.config.ts
18166
+ var BAR_SIZE_MIN = 24;
18167
+ var BAR_SIZE_MAX = 80;
18168
+ var BAR_GAP = 6;
18169
+ var GROUP_PADDING_MIN = 8;
18170
+ var GROUP_PADDING_MAX = 24;
18171
+ var VALUE_LABEL_GAP = 6;
18172
+ var BAR_RADIUS = 8;
18173
+ var BAR_OPACITY_DEFAULT = "var(--opacity-080)";
18174
+ var BAR_OPACITY_DIMMED = "var(--opacity-060)";
18175
+ var BAR_VALUE_COLOR = "var(--sys-color-field-text-readOnly)";
18176
+ function bandLayout(size, categoryCount, seriesCount, barSizeMax = BAR_SIZE_MAX) {
18177
+ const categories = Math.max(1, categoryCount);
18178
+ const series = Math.max(1, seriesCount);
18179
+ const gaps = (series - 1) * BAR_GAP;
18180
+ const barRange = BAR_SIZE_MAX - BAR_SIZE_MIN;
18181
+ const padRange = GROUP_PADDING_MAX - GROUP_PADDING_MIN;
18182
+ const maxBar = Math.min(Math.max(BAR_SIZE_MIN, barSizeMax), BAR_SIZE_MAX);
18183
+ const tMax = (maxBar - BAR_SIZE_MIN) / barRange;
18184
+ const bandMin = BAR_SIZE_MIN * series + gaps + GROUP_PADDING_MIN * 2;
18185
+ const span = barRange * series + padRange * 2;
18186
+ const band = Math.min(size / categories, bandMin + span * tMax);
18187
+ const t = Math.min(Math.max((band - bandMin) / span, 0), tMax);
18188
+ const bar = BAR_SIZE_MIN + barRange * t;
18189
+ const padding = GROUP_PADDING_MIN + padRange * t;
18190
+ const group = bar * series + gaps;
18191
+ return {
18192
+ band,
18193
+ bar,
18194
+ group,
18195
+ padding,
18196
+ offset: Math.max(0, (size - band * categories) / 2)
18197
+ };
18198
+ }
18199
+ var defaultFormat = (value) => value.toLocaleString();
18200
+ function minBand(seriesCount) {
18201
+ const series = Math.max(1, seriesCount);
18202
+ return series * BAR_SIZE_MIN + (series - 1) * BAR_GAP + GROUP_PADDING_MIN * 2;
18203
+ }
18204
+ function barPath(x, y, width, height, direction, negative) {
18205
+ const r = Math.max(0, Math.min(BAR_RADIUS, width / 2, height / 2));
18206
+ if (r === 0) return `M${x} ${y}h${width}v${height}h${-width}Z`;
18207
+ if (direction === "vertical") {
18208
+ return negative ? (
18209
+ // 아래로 자란다 — 아래쪽 두 모서리가 둥글다
18210
+ `M${x} ${y}h${width}v${height - r}a${r} ${r} 0 0 1 ${-r} ${r}h${-(width - 2 * r)}a${r} ${r} 0 0 1 ${-r} ${-r}Z`
18211
+ ) : (
18212
+ // 위로 자란다 — 위쪽 두 모서리가 둥글다
18213
+ `M${x} ${y + height}v${-(height - r)}a${r} ${r} 0 0 1 ${r} ${-r}h${width - 2 * r}a${r} ${r} 0 0 1 ${r} ${r}v${height - r}Z`
18214
+ );
18215
+ }
18216
+ return negative ? (
18217
+ // 왼쪽으로 자란다
18218
+ `M${x + width} ${y}h${-(width - r)}a${r} ${r} 0 0 0 ${-r} ${r}v${height - 2 * r}a${r} ${r} 0 0 0 ${r} ${r}h${width - r}Z`
18219
+ ) : (
18220
+ // 오른쪽으로 자란다
18221
+ `M${x} ${y}h${width - r}a${r} ${r} 0 0 1 ${r} ${r}v${height - 2 * r}a${r} ${r} 0 0 1 ${-r} ${r}h${-(width - r)}Z`
18222
+ );
18223
+ }
18224
+ var SBarChart = /* @__PURE__ */ forwardRef(function SBarChart2({
18225
+ categories,
18226
+ series,
18227
+ direction = "vertical",
18228
+ palette = "default",
18229
+ colors: customColors,
18230
+ max,
18231
+ tickCount = 8,
18232
+ barSize = BAR_SIZE_MAX,
18233
+ showValueLabel = true,
18234
+ showLegend = true,
18235
+ legendPosition = "top",
18236
+ showTooltip = true,
18237
+ formatValue = defaultFormat,
18238
+ height,
18239
+ onBarClick,
18240
+ className,
18241
+ style
18242
+ }, ref) {
18243
+ const vertical = direction === "vertical";
18244
+ const { ref: plotRef, size } = useElementSize();
18245
+ const { ref: axisTextRef, size: axisText } = useElementSize();
18246
+ const { ref: axisSampleRef, size: axisSample } = useElementSize();
18247
+ const [hovered, setHovered] = useState(null);
18248
+ const hoveredCategory = hovered?.category ?? null;
18249
+ const perCategory = series.length === 1;
18250
+ const colorAt = useCallback(
18251
+ (seriesIndex, categoryIndex) => paletteColor(palette, perCategory ? categoryIndex : seriesIndex, customColors),
18252
+ [palette, customColors, perCategory]
18253
+ );
18254
+ const scale = useMemo(
18255
+ () => niceScale(
18256
+ series.flatMap((s) => s.data),
18257
+ tickCount,
18258
+ max
18259
+ ),
18260
+ [series, tickCount, max]
18261
+ );
18262
+ const axisLength = vertical ? size.width : size.height;
18263
+ const layout = useMemo(
18264
+ () => bandLayout(axisLength, categories.length, series.length, barSize),
18265
+ [axisLength, categories.length, series.length, barSize]
18266
+ );
18267
+ const zeroRatio = ratioOf(0, scale);
18268
+ const minPlotWidth = vertical ? categories.length * minBand(series.length) : 0;
18269
+ const totalHeight = height ?? (vertical ? 320 : void 0);
18270
+ const plotHeight = totalHeight == null ? Math.max(1, categories.length) * minBand(series.length) : typeof totalHeight === "number" ? Math.max(0, totalHeight - axisText.height) : `calc(${totalHeight} - ${axisText.height}px)`;
18271
+ const legend = showLegend ? /* @__PURE__ */ jsx(ChartLegend, { items: series.map((s, i) => ({ name: s.name, color: colorAt(i, 0) })) }) : null;
18272
+ const handleMove = (event) => {
18273
+ if (!showTooltip || categories.length === 0 || series.length === 0) return;
18274
+ const rect = event.currentTarget.getBoundingClientRect();
18275
+ const x = event.clientX - rect.left;
18276
+ const y = event.clientY - rect.top;
18277
+ const along = (vertical ? x : y) - layout.offset;
18278
+ const category = Math.floor(along / layout.band);
18279
+ if (category < 0 || category >= categories.length) {
18280
+ setHovered(null);
18281
+ return;
18282
+ }
18283
+ setHovered({ category, x, y });
18284
+ };
18285
+ const tooltipItems = hovered == null ? [] : series.flatMap((s, i) => {
18286
+ const value = s.data[hovered.category];
18287
+ if (value == null || !Number.isFinite(value)) return [];
18288
+ return [
18289
+ { color: colorAt(i, hovered.category), label: s.name, value: formatValue(value) }
18290
+ ];
18291
+ });
18292
+ const categoryLabels = /* @__PURE__ */ jsxs(
18293
+ "div",
18294
+ {
18295
+ "aria-hidden": true,
18296
+ className: cn(
18297
+ "relative typo-body-sm-medium text-(--cmp-chart-axis-color)",
18298
+ vertical ? "w-full" : "h-full"
18299
+ ),
18300
+ style: vertical ? { minWidth: minPlotWidth || void 0 } : { height: plotHeight },
18301
+ children: [
18302
+ categories.map((label, index) => {
18303
+ const center = layout.offset + (index + 0.5) * layout.band;
18304
+ return vertical ? /* @__PURE__ */ jsx(
18305
+ "span",
18306
+ {
18307
+ className: "absolute top-0 -translate-x-1/2 whitespace-nowrap",
18308
+ style: { left: center },
18309
+ children: label
18310
+ },
18311
+ `${label}-${index}`
18312
+ ) : /* @__PURE__ */ jsx(
18313
+ "span",
18314
+ {
18315
+ className: "absolute right-0 -translate-y-1/2 whitespace-nowrap",
18316
+ style: { top: center },
18317
+ children: label
18318
+ },
18319
+ `${label}-${index}`
18320
+ );
18321
+ }),
18322
+ /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "invisible block whitespace-nowrap", children: vertical ? "0" : categories.reduce((longest, c) => c.length > longest.length ? c : longest, "") })
18323
+ ]
18324
+ }
18325
+ );
18326
+ const bars = useMemo(
18327
+ () => size.width > 0 && size.height > 0 ? series.flatMap(
18328
+ (s, seriesIndex) => categories.map((_, categoryIndex) => {
18329
+ const value = s.data[categoryIndex];
18330
+ if (value == null || !Number.isFinite(value)) return null;
18331
+ const dimmed = hoveredCategory != null && hoveredCategory !== categoryIndex;
18332
+ const groupStart = layout.offset + categoryIndex * layout.band + (layout.band - layout.group) / 2;
18333
+ const offset = groupStart + seriesIndex * (layout.bar + BAR_GAP);
18334
+ const valueRatio = ratioOf(value, scale);
18335
+ const negative = value < 0;
18336
+ let x;
18337
+ let y;
18338
+ let width;
18339
+ let barHeight;
18340
+ let labelX;
18341
+ let labelY;
18342
+ if (vertical) {
18343
+ const zeroY = size.height - zeroRatio * size.height;
18344
+ const valueY = size.height - valueRatio * size.height;
18345
+ x = offset;
18346
+ width = layout.bar;
18347
+ y = Math.min(zeroY, valueY);
18348
+ barHeight = Math.abs(zeroY - valueY);
18349
+ labelX = offset + layout.bar / 2;
18350
+ labelY = negative ? y + barHeight + VALUE_LABEL_GAP + 10 : y - VALUE_LABEL_GAP;
18351
+ } else {
18352
+ const zeroX = zeroRatio * size.width;
18353
+ const valueX = valueRatio * size.width;
18354
+ x = Math.min(zeroX, valueX);
18355
+ width = Math.abs(zeroX - valueX);
18356
+ y = offset;
18357
+ barHeight = layout.bar;
18358
+ labelX = negative ? x - VALUE_LABEL_GAP : x + width + VALUE_LABEL_GAP;
18359
+ labelY = offset + layout.bar / 2;
18360
+ }
18361
+ return /* @__PURE__ */ jsxs("g", { children: [
18362
+ /* @__PURE__ */ jsx(
18363
+ "path",
18364
+ {
18365
+ d: barPath(x, y, width, barHeight, direction, negative),
18366
+ style: {
18367
+ fill: colorAt(seriesIndex, categoryIndex),
18368
+ opacity: dimmed ? BAR_OPACITY_DIMMED : BAR_OPACITY_DEFAULT,
18369
+ cursor: onBarClick ? "pointer" : void 0
18370
+ },
18371
+ onClick: onBarClick ? () => onBarClick({ seriesIndex, categoryIndex, value }) : void 0,
18372
+ children: /* @__PURE__ */ jsx("title", { children: `${categories[categoryIndex]} ${s.name} ${formatValue(value)}` })
18373
+ }
18374
+ ),
18375
+ showValueLabel && /* @__PURE__ */ jsx(
18376
+ "text",
18377
+ {
18378
+ x: labelX,
18379
+ y: labelY,
18380
+ className: "typo-body-sm-medium",
18381
+ style: { fill: BAR_VALUE_COLOR },
18382
+ textAnchor: vertical ? "middle" : negative ? "end" : "start",
18383
+ dominantBaseline: vertical ? "auto" : "central",
18384
+ children: formatValue(value)
18385
+ }
18386
+ )
18387
+ ] }, `${seriesIndex}-${categoryIndex}`);
18388
+ })
18389
+ ) : [],
18390
+ [
18391
+ size.width,
18392
+ size.height,
18393
+ series,
18394
+ categories,
18395
+ layout,
18396
+ scale,
18397
+ zeroRatio,
18398
+ vertical,
18399
+ direction,
18400
+ showValueLabel,
18401
+ formatValue,
18402
+ onBarClick,
18403
+ colorAt,
18404
+ hoveredCategory
18405
+ ]
18406
+ );
18407
+ const axisLabels = /* @__PURE__ */ jsx(
18408
+ ChartAxisLabels,
18409
+ {
18410
+ scale,
18411
+ direction,
18412
+ format: formatValue,
18413
+ sampleRef: axisSampleRef
18414
+ }
18415
+ );
18416
+ const inset = vertical ? { paddingTop: axisSample.height / 2, paddingBottom: axisSample.height / 2 } : { paddingLeft: axisSample.width / 2, paddingRight: axisSample.width / 2 };
18417
+ const plot = (
18418
+ // 바깥은 자리(높이·물러남)를, 안쪽은 그림을 맡는다. minWidth 는 바깥에 둬야
18419
+ // 스크롤 컨테이너가 넘치는 폭을 알아본다 — 안쪽에 두면 바깥이 부모 폭에 맞춰져
18420
+ // 스크롤이 생기지 않는다.
18421
+ /* @__PURE__ */ jsx(
18422
+ "div",
18423
+ {
18424
+ className: "relative min-w-0",
18425
+ style: { height: plotHeight, minWidth: minPlotWidth || void 0, ...inset },
18426
+ children: /* @__PURE__ */ jsxs(
18427
+ "div",
18428
+ {
18429
+ ref: plotRef,
18430
+ className: "relative h-full w-full",
18431
+ onMouseMove: handleMove,
18432
+ onMouseLeave: () => setHovered(null),
18433
+ children: [
18434
+ size.width > 0 && size.height > 0 && /* @__PURE__ */ jsxs(
18435
+ "svg",
18436
+ {
18437
+ width: size.width,
18438
+ height: size.height,
18439
+ role: "img",
18440
+ "aria-label": `${categories.length}\uAC1C \uD56D\uBAA9, ${series.length}\uAC1C \uACC4\uC5F4 \uB9C9\uB300 \uADF8\uB798\uD504`,
18441
+ children: [
18442
+ /* @__PURE__ */ jsx(
18443
+ ChartGrid,
18444
+ {
18445
+ scale,
18446
+ direction,
18447
+ width: size.width,
18448
+ height: size.height
18449
+ }
18450
+ ),
18451
+ hovered != null && /* @__PURE__ */ jsx(
18452
+ "rect",
18453
+ {
18454
+ "aria-hidden": true,
18455
+ x: vertical ? layout.offset + hovered.category * layout.band : 0,
18456
+ y: vertical ? 0 : layout.offset + hovered.category * layout.band,
18457
+ width: vertical ? layout.band : size.width,
18458
+ height: vertical ? size.height : layout.band,
18459
+ style: {
18460
+ fill: "var(--cmp-chart-barContainer-hover-bg)",
18461
+ fillOpacity: "var(--cmp-chart-barContainer-hover-opacity)"
18462
+ }
18463
+ }
18464
+ ),
18465
+ bars
18466
+ ]
18467
+ }
18468
+ ),
18469
+ showTooltip && hovered != null && series.length > 0 && /* @__PURE__ */ jsx(
18470
+ ChartTooltip,
18471
+ {
18472
+ title: categories[hovered.category] ?? "",
18473
+ items: tooltipItems,
18474
+ x: hovered.x,
18475
+ y: hovered.y,
18476
+ bounds: size
18477
+ }
18478
+ )
18479
+ ]
18480
+ }
18481
+ )
18482
+ }
18483
+ )
18484
+ );
18485
+ return /* @__PURE__ */ jsx(
18486
+ ChartFrame,
18487
+ {
18488
+ ref,
18489
+ legend,
18490
+ legendPosition,
18491
+ className,
18492
+ style,
18493
+ children: /* @__PURE__ */ jsx(
18494
+ "div",
18495
+ {
18496
+ className: "grid",
18497
+ style: {
18498
+ columnGap: AXIS_LABEL_GAP,
18499
+ // 눈금은 왼쪽 한 줄뿐이다 — 오른쪽에도 적으면 서로 다른 단위처럼 읽힌다.
18500
+ gridTemplateColumns: "max-content 1fr"
18501
+ },
18502
+ children: vertical ? /* @__PURE__ */ jsxs(Fragment, { children: [
18503
+ /* @__PURE__ */ jsx("div", { style: { height: plotHeight, ...inset }, children: /* @__PURE__ */ jsx(
18504
+ ChartAxisLabels,
18505
+ {
18506
+ scale,
18507
+ direction,
18508
+ format: formatValue,
18509
+ sampleRef: axisSampleRef
18510
+ }
18511
+ ) }),
18512
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 overflow-x-auto", children: [
18513
+ plot,
18514
+ /* @__PURE__ */ jsx("div", { ref: axisTextRef, children: categoryLabels })
18515
+ ] })
18516
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
18517
+ categoryLabels,
18518
+ plot,
18519
+ /* @__PURE__ */ jsx("div", {}),
18520
+ /* @__PURE__ */ jsx("div", { ref: axisTextRef, style: inset, children: axisLabels })
18521
+ ] })
18522
+ }
18523
+ )
18524
+ }
18525
+ );
18526
+ });
17852
18527
  var BUTTON_STYLE = {
17853
18528
  default: { color: "secondary" },
17854
18529
  danger: { color: "danger", outline: true },
@@ -22747,15 +23422,15 @@ var SBarcodeInput = /* @__PURE__ */ forwardRef(
22747
23422
  );
22748
23423
 
22749
23424
  // src/components/STimePicker/timepicker.config.ts
22750
- var V3 = (path) => `var(--cmp-timepicker-${path})`;
23425
+ var V4 = (path) => `var(--cmp-timepicker-${path})`;
22751
23426
  var TIMEPICKER_SIZES = ["sm", "md"];
22752
23427
  var TIMEPICKER_SIZE_CONFIG = {
22753
23428
  sm: {
22754
- height: V3("sm-height"),
22755
- paddingX: V3("sm-paddingX"),
22756
- gap: V3("sm-gap"),
22757
- icon: V3("sm-icon"),
22758
- radius: V3("sm-radius"),
23429
+ height: V4("sm-height"),
23430
+ paddingX: V4("sm-paddingX"),
23431
+ gap: V4("sm-gap"),
23432
+ icon: V4("sm-icon"),
23433
+ radius: V4("sm-radius"),
22759
23434
  // TODO: component.timepicker 토큰에 typography가 추가되면 CSS 변수로 교체한다.
22760
23435
  fontSize: 12,
22761
23436
  lineHeight: 20,
@@ -22764,11 +23439,11 @@ var TIMEPICKER_SIZE_CONFIG = {
22764
23439
  fieldMaxWidth: "md"
22765
23440
  },
22766
23441
  md: {
22767
- height: V3("md-height"),
22768
- paddingX: V3("md-paddingX"),
22769
- gap: V3("md-gap"),
22770
- icon: V3("md-icon"),
22771
- radius: V3("md-radius"),
23442
+ height: V4("md-height"),
23443
+ paddingX: V4("md-paddingX"),
23444
+ gap: V4("md-gap"),
23445
+ icon: V4("md-icon"),
23446
+ radius: V4("md-radius"),
22772
23447
  // TODO: component.timepicker 토큰에 typography가 추가되면 CSS 변수로 교체한다.
22773
23448
  fontSize: 14,
22774
23449
  lineHeight: 24,
@@ -23658,6 +24333,6 @@ function STimeRangePicker({
23658
24333
  );
23659
24334
  }
23660
24335
 
23661
- export { ACCOUNT_LIST_BOX_ICONS, ACCOUNT_LIST_BOX_ITEM_TONES, ACCOUNT_LIST_BOX_LABELS, ACCOUNT_LIST_BOX_LAYOUT, ACCOUNT_LIST_BOX_TONE_CONFIG, BADGE_COLORS, BUTTON_COLORS, BUTTON_SIZES, CALENDAR_BOARD_DAY_BG, CALENDAR_BOARD_DAY_COLORS, CALENDAR_BOARD_EVENT_COLORS, CALENDAR_BOARD_EVENT_PRESETS, CALENDAR_BOARD_MAX_VISIBLE_EVENTS, CHAT_ATTACHED_FILE_CONFIG, CHAT_FILE_CONFIG, CHAT_MESSAGE_DIRECTIONS, CHAT_MESSAGE_READ_STATUSES, CHAT_MESSAGE_VARIANTS, COLOR_KEYS, FIELD_WIDTH_GRADES, GNB_COLORS, GNB_COMMON, GNB_FOLD_MS, GNB_FULL_LOGO_WIDTH, GNB_HEADER, GNB_MENU, GNB_MENU_WIDTH, GNB_RAIL, GNB_RAIL_WIDTH, GNB_SYSTEM_COLORS, GNB_SYSTEM_COLOR_CONFIG, GNB_SYSTEM_LAYOUT, GNB_SYSTEM_PLACEMENTS, GNB_SYSTEM_TOP_COLOR_CONFIG, ICONS, IMAGE_FITS, PAGE_BACKGROUNDS, PAGE_CONTENT_HEIGHTS, PAGE_CONTENT_WIDTHS, PAGE_HEADER_VARIANTS, RangeCalendar, SAccountListBox, SActionModal, SBadge, SBarcodeInput, SButton, SCROLL_AREA_AXES, SCalendar, SCalendarBoard, SCallout, SCard, SChatAttachedFile2 as SChatAttachedFile, SChatFile2 as SChatFile, SChatInput2 as SChatInput, SChatMessage2 as SChatMessage, SChatSystemMessage2 as SChatSystemMessage, SCheckbox, SChip2 as SChip, SChipFilter, SChipInput, SCircleProgress, SConfirmModal, SDatePicker, SDatePickerMonthListbox, SDatePickerYearListbox, SDateRangePicker, SDivider, SDraggableGroup, SDraggableItem2 as SDraggableItem, SDraggableList, SDraggableProvider, SDrawer, SDropdownButton2 as SDropdownButton, SEDITOR_DEFAULT_FONT_SIZE, SEDITOR_HEADING_LABELS, SEDITOR_HEADING_LEVELS, SEditor, SExpansionItem, SExpansionList2 as SExpansionList, SField, SFilePicker, SForm, SGhostButton, SGnb, SGnbSystem, SGnbSystemContext, SGuide, SIcon, SImage, SInput, SKeyValueTable, SLAYOUT_MIN_WIDTH, SLayout, SLayoutContext, SLinearProgress, SList2 as SList, SListItem2 as SListItem, SLoadingContainer, SLoadingModal, SLoadingViewport, SLoginCard2 as SLoginCard, SModal, SModalOutlet, SNumberInput, SPage, SPageHeader, SPagination, SPopover, SPopup, SPortal, SRadio, SRadioButton, SRadioCard, SRadioCardGroup, SRadioGroup, SSPLITTER_UNITS, SScrollArea, SSearchInput, SSectionHeaderCard2 as SSectionHeaderCard, SSelect2 as SSelect, SSplitter2 as SSplitter, SStepper, SSwitch, SSystemActionButton, STEPPER_SIZES, STable, STableBar, STabs, STag, STextLink, STextarea, STimePicker, STimeRangePicker, SToast, SToastContainer, SToggle, STooltip2 as STooltip, STree2 as STree, STreeItem2 as STreeItem, SYSTEM_ACTION_BUTTON_COLORS, SYSTEM_ACTION_BUTTON_COLOR_CONFIG, SYSTEM_ACTION_BUTTON_LAYOUT, SYSTEM_ACTION_BUTTON_TYPES, SYSTEM_ACTION_BUTTON_TYPE_CONFIG, TAG_COLORS, TAG_SHAPES, TAG_SIZES, TIMEPICKER_SIZES, TIMEPICKER_SIZE_CONFIG, buttonPreset, cn, fieldWidthValue, fileExtension, findGnbAncestors, findGnbRailValue, formatFileSize, isColorKey, isFieldWidthGrade, loading, normalizeExtension, resolveColor, useSLayout };
24336
+ export { ACCOUNT_LIST_BOX_ICONS, ACCOUNT_LIST_BOX_ITEM_TONES, ACCOUNT_LIST_BOX_LABELS, ACCOUNT_LIST_BOX_LAYOUT, ACCOUNT_LIST_BOX_TONE_CONFIG, BADGE_COLORS, BAR_GAP, BAR_SIZE_MAX, BAR_SIZE_MIN, BUTTON_COLORS, BUTTON_SIZES, CALENDAR_BOARD_DAY_BG, CALENDAR_BOARD_DAY_COLORS, CALENDAR_BOARD_EVENT_COLORS, CALENDAR_BOARD_EVENT_PRESETS, CALENDAR_BOARD_MAX_VISIBLE_EVENTS, CHAT_ATTACHED_FILE_CONFIG, CHAT_FILE_CONFIG, CHAT_MESSAGE_DIRECTIONS, CHAT_MESSAGE_READ_STATUSES, CHAT_MESSAGE_VARIANTS, COLOR_KEYS, FIELD_WIDTH_GRADES, GNB_COLORS, GNB_COMMON, GNB_FOLD_MS, GNB_FULL_LOGO_WIDTH, GNB_HEADER, GNB_MENU, GNB_MENU_WIDTH, GNB_RAIL, GNB_RAIL_WIDTH, GNB_SYSTEM_COLORS, GNB_SYSTEM_COLOR_CONFIG, GNB_SYSTEM_LAYOUT, GNB_SYSTEM_PLACEMENTS, GNB_SYSTEM_TOP_COLOR_CONFIG, ICONS, IMAGE_FITS, PAGE_BACKGROUNDS, PAGE_CONTENT_HEIGHTS, PAGE_CONTENT_WIDTHS, PAGE_HEADER_VARIANTS, RangeCalendar, SAccountListBox, SActionModal, SBadge, SBarChart, SBarcodeInput, SButton, SCROLL_AREA_AXES, SCalendar, SCalendarBoard, SCallout, SCard, SChatAttachedFile2 as SChatAttachedFile, SChatFile2 as SChatFile, SChatInput2 as SChatInput, SChatMessage2 as SChatMessage, SChatSystemMessage2 as SChatSystemMessage, SCheckbox, SChip2 as SChip, SChipFilter, SChipInput, SCircleProgress, SConfirmModal, SDatePicker, SDatePickerMonthListbox, SDatePickerYearListbox, SDateRangePicker, SDivider, SDraggableGroup, SDraggableItem2 as SDraggableItem, SDraggableList, SDraggableProvider, SDrawer, SDropdownButton2 as SDropdownButton, SEDITOR_DEFAULT_FONT_SIZE, SEDITOR_HEADING_LABELS, SEDITOR_HEADING_LEVELS, SEditor, SExpansionItem, SExpansionList2 as SExpansionList, SField, SFilePicker, SForm, SGhostButton, SGnb, SGnbSystem, SGnbSystemContext, SGuide, SIcon, SImage, SInput, SKeyValueTable, SLAYOUT_MIN_WIDTH, SLayout, SLayoutContext, SLinearProgress, SList2 as SList, SListItem2 as SListItem, SLoadingContainer, SLoadingModal, SLoadingViewport, SLoginCard2 as SLoginCard, SModal, SModalOutlet, SNumberInput, SPage, SPageHeader, SPagination, SPopover, SPopup, SPortal, SRadio, SRadioButton, SRadioCard, SRadioCardGroup, SRadioGroup, SSPLITTER_UNITS, SScrollArea, SSearchInput, SSectionHeaderCard2 as SSectionHeaderCard, SSelect2 as SSelect, SSplitter2 as SSplitter, SStepper, SSwitch, SSystemActionButton, STEPPER_SIZES, STable, STableBar, STabs, STag, STextLink, STextarea, STimePicker, STimeRangePicker, SToast, SToastContainer, SToggle, STooltip2 as STooltip, STree2 as STree, STreeItem2 as STreeItem, SYSTEM_ACTION_BUTTON_COLORS, SYSTEM_ACTION_BUTTON_COLOR_CONFIG, SYSTEM_ACTION_BUTTON_LAYOUT, SYSTEM_ACTION_BUTTON_TYPES, SYSTEM_ACTION_BUTTON_TYPE_CONFIG, TAG_COLORS, TAG_SHAPES, TAG_SIZES, TIMEPICKER_SIZES, TIMEPICKER_SIZE_CONFIG, buttonPreset, cn, fieldWidthValue, fileExtension, findGnbAncestors, findGnbRailValue, formatFileSize, isColorKey, isFieldWidthGrade, loading, normalizeExtension, resolveColor, useSLayout };
23662
24337
  //# sourceMappingURL=index.js.map
23663
24338
  //# sourceMappingURL=index.js.map