sellmate-design-system-react 9.0.0-beta.47 → 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/AGENTS.md +21 -5
- package/dist/components/SBarChart/README.md +104 -0
- package/dist/components/SBarChart/SBarChart.d.ts +75 -0
- package/dist/components/SBarChart/barChart.config.d.ts +65 -0
- package/dist/components/SBarChart/index.d.ts +2 -0
- package/dist/components/SChatAttachedFile/README.md +3 -4
- package/dist/components/SChatAttachedFile/SChatAttachedFile.d.ts +3 -44
- package/dist/components/SChatAttachedFile/chatAttachedFile.config.d.ts +19 -39
- package/dist/components/SChatAttachedFile/index.d.ts +1 -1
- package/dist/components/SChatFile/README.md +33 -0
- package/dist/components/SChatFile/SChatFile.d.ts +37 -0
- package/dist/components/SChatFile/chatFile.config.d.ts +56 -0
- package/dist/components/SChatFile/index.d.ts +2 -0
- package/dist/components/SChatMessage/README.md +9 -5
- package/dist/components/SChatMessage/SChatMessage.d.ts +22 -7
- package/dist/components/SGhostButton/README.md +2 -0
- package/dist/components/SIcon/README.md +2 -0
- package/dist/index.cjs +944 -132
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +939 -132
- package/dist/index.js.map +1 -1
- package/dist/lib/chart/cartesian.d.ts +46 -0
- package/dist/lib/chart/frame.d.ts +17 -0
- package/dist/lib/chart/legend.d.ts +15 -0
- package/dist/lib/chart/palette.d.ts +26 -0
- package/dist/lib/chart/scale.d.ts +26 -0
- package/dist/lib/chart/tooltip.d.ts +23 -0
- package/dist/lib/chart/types.d.ts +29 -0
- package/dist/lib/chart/use-element-size.d.ts +17 -0
- package/dist/llms-full.txt +171 -16
- package/dist/llms.txt +25 -10
- package/dist/styles.css +347 -40
- package/dist/theme.css +118 -7
- package/eslint/scale.gen.mjs +2 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4653,10 +4653,9 @@ var STextarea = /* @__PURE__ */ forwardRef(function STextarea2({
|
|
|
4653
4653
|
);
|
|
4654
4654
|
});
|
|
4655
4655
|
var DEFAULT_PLACEHOLDER = "\uBA54\uC2DC\uC9C0\uB97C \uC785\uB825\uD574 \uC8FC\uC138\uC694...";
|
|
4656
|
-
var FIELD_MIN_HEIGHT = 48;
|
|
4657
4656
|
var FIELD_BORDER = 1;
|
|
4658
|
-
var TEXTAREA_MIN_HEIGHT =
|
|
4659
|
-
var BUTTON_BOTTOM_INSET = `calc((
|
|
4657
|
+
var TEXTAREA_MIN_HEIGHT = `calc(var(--cmp-chatInput-buttonFrame-height) - ${FIELD_BORDER * 2}px)`;
|
|
4658
|
+
var BUTTON_BOTTOM_INSET = `calc((var(--cmp-chatInput-buttonFrame-height) - var(--cmp-ghostButton-md-size)) / 2)`;
|
|
4660
4659
|
var DEFAULT_MAX_ROWS = 5;
|
|
4661
4660
|
var SChatInputImpl = /* @__PURE__ */ forwardRef(function SChatInput({
|
|
4662
4661
|
value = "",
|
|
@@ -4685,8 +4684,8 @@ var SChatInputImpl = /* @__PURE__ */ forwardRef(function SChatInput({
|
|
|
4685
4684
|
e.preventDefault();
|
|
4686
4685
|
send();
|
|
4687
4686
|
};
|
|
4688
|
-
return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col bg-(--
|
|
4689
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-end gap-
|
|
4687
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col bg-(--cmp-chatInput-bg)", className), style, children: [
|
|
4688
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-end gap-(--cmp-chatInput-gap) p-(--cmp-chatInput-padding)", children: [
|
|
4690
4689
|
onAttach != null && /* @__PURE__ */ jsx(
|
|
4691
4690
|
SGhostButton,
|
|
4692
4691
|
{
|
|
@@ -4748,81 +4747,67 @@ var SChatInputImpl = /* @__PURE__ */ forwardRef(function SChatInput({
|
|
|
4748
4747
|
var SChatInput2 = /* @__PURE__ */ withDisplayName(SChatInputImpl, "SChatInput");
|
|
4749
4748
|
|
|
4750
4749
|
// src/components/SChatAttachedFile/chatAttachedFile.config.ts
|
|
4751
|
-
var CHAT_ATTACHED_FILE_VARIANTS = ["attached", "sent"];
|
|
4752
4750
|
var CHAT_ATTACHED_FILE_CONFIG = {
|
|
4753
|
-
|
|
4754
|
-
|
|
4755
|
-
|
|
4756
|
-
|
|
4757
|
-
|
|
4758
|
-
|
|
4759
|
-
|
|
4760
|
-
|
|
4761
|
-
|
|
4762
|
-
|
|
4763
|
-
|
|
4764
|
-
|
|
4765
|
-
|
|
4751
|
+
/** 바깥 상자 — 좌우·상하 여백·면·선·라운드 */
|
|
4752
|
+
box: "px-(--cmp-chatAttachedFile-paddingX) py-(--cmp-chatAttachedFile-paddingY) rounded-(--cmp-chatAttachedFile-radius) border-(length:--cmp-chatAttachedFile-border-width) border-(--cmp-chatAttachedFile-border-color) bg-(--cmp-chatAttachedFile-bg)",
|
|
4753
|
+
/** 콘텐츠 묶음 ↔ 제거 버튼 간격 */
|
|
4754
|
+
outerGap: "gap-(--cmp-chatAttachedFile-gap)",
|
|
4755
|
+
/** 아이콘 칩 ↔ 파일명 간격 */
|
|
4756
|
+
contentGap: "gap-(--cmp-chatAttachedFile-content-gap)",
|
|
4757
|
+
/** 클립 아이콘이 앉는 칩 — 내부 패딩·면·라운드 (높이는 아이콘+패딩이 정한다) */
|
|
4758
|
+
tile: "p-(--cmp-chatAttachedFile-icon-wrapper-padding) rounded-(--cmp-chatAttachedFile-icon-wrapper-radius) bg-(--cmp-chatAttachedFile-icon-wrapper-bg)",
|
|
4759
|
+
/** 칩 안 클립 아이콘 크기(px). SVG width/height 는 속성값이라 CSS 변수를 못 받아 값으로 둔다 (토큰 값과 동일) */
|
|
4760
|
+
iconSize: 16,
|
|
4761
|
+
/** 파일명 타이포 프리셋 */
|
|
4762
|
+
nameTypo: "typo-body-sm-default",
|
|
4763
|
+
/** 파일명 텍스트색 */
|
|
4764
|
+
textColor: "text-(--cmp-chatAttachedFile-filename-color)"
|
|
4766
4765
|
};
|
|
4767
|
-
var SIZE_UNITS = ["B", "KB", "MB", "GB", "TB"];
|
|
4768
|
-
function formatFileSize(bytes) {
|
|
4769
|
-
if (!Number.isFinite(bytes) || bytes < 0) return "";
|
|
4770
|
-
let value = bytes;
|
|
4771
|
-
let unit = 0;
|
|
4772
|
-
while (value >= 1024 && unit < SIZE_UNITS.length - 1) {
|
|
4773
|
-
value /= 1024;
|
|
4774
|
-
unit += 1;
|
|
4775
|
-
}
|
|
4776
|
-
return unit === 0 ? `${Math.round(value)}${SIZE_UNITS[0]}` : `${value.toFixed(1)}${SIZE_UNITS[unit]}`;
|
|
4777
|
-
}
|
|
4778
|
-
function normalizeExtension(extension) {
|
|
4779
|
-
return extension.trim().replace(/^\.+/, "").toLowerCase();
|
|
4780
|
-
}
|
|
4781
|
-
function fileExtension(name) {
|
|
4782
|
-
const dot = name.lastIndexOf(".");
|
|
4783
|
-
return dot > 0 && dot < name.length - 1 ? name.slice(dot + 1).toLowerCase() : "";
|
|
4784
|
-
}
|
|
4785
4766
|
var SChatAttachedFileImpl = /* @__PURE__ */ forwardRef(
|
|
4786
|
-
function SChatAttachedFile({
|
|
4787
|
-
const preset = CHAT_ATTACHED_FILE_CONFIG[variant];
|
|
4788
|
-
const ext = extension != null ? normalizeExtension(extension) : fileExtension(name);
|
|
4789
|
-
const meta = [ext, size != null ? formatFileSize(size) : ""].filter(Boolean).join(" / ");
|
|
4790
|
-
const showMeta = variant === "sent" && meta !== "";
|
|
4791
|
-
const showRemove = variant === "attached" && onRemove != null;
|
|
4767
|
+
function SChatAttachedFile({ name, onRemove, className, ...rest }, ref) {
|
|
4792
4768
|
return /* @__PURE__ */ jsxs(
|
|
4793
4769
|
"div",
|
|
4794
4770
|
{
|
|
4795
4771
|
ref,
|
|
4796
4772
|
className: cn(
|
|
4797
|
-
"inline-flex max-w-full items-center
|
|
4798
|
-
|
|
4773
|
+
"inline-flex max-w-full items-center border-solid",
|
|
4774
|
+
CHAT_ATTACHED_FILE_CONFIG.box,
|
|
4775
|
+
CHAT_ATTACHED_FILE_CONFIG.outerGap,
|
|
4799
4776
|
className
|
|
4800
4777
|
),
|
|
4801
4778
|
...rest,
|
|
4802
4779
|
children: [
|
|
4803
|
-
/* @__PURE__ */
|
|
4804
|
-
|
|
4805
|
-
|
|
4806
|
-
|
|
4807
|
-
|
|
4808
|
-
|
|
4809
|
-
|
|
4810
|
-
|
|
4811
|
-
|
|
4812
|
-
|
|
4813
|
-
|
|
4814
|
-
|
|
4815
|
-
|
|
4816
|
-
|
|
4780
|
+
/* @__PURE__ */ jsxs("span", { className: cn("flex min-w-0 items-center", CHAT_ATTACHED_FILE_CONFIG.contentGap), children: [
|
|
4781
|
+
/* @__PURE__ */ jsx(
|
|
4782
|
+
"span",
|
|
4783
|
+
{
|
|
4784
|
+
className: cn(
|
|
4785
|
+
"inline-grid shrink-0 place-items-center text-(--sys-color-fg-accentLight)",
|
|
4786
|
+
CHAT_ATTACHED_FILE_CONFIG.tile
|
|
4787
|
+
),
|
|
4788
|
+
children: /* @__PURE__ */ jsx(SIcon, { name: "attachFile", color: "grey_65", size: CHAT_ATTACHED_FILE_CONFIG.iconSize })
|
|
4789
|
+
}
|
|
4790
|
+
),
|
|
4791
|
+
/* @__PURE__ */ jsx(
|
|
4792
|
+
"span",
|
|
4793
|
+
{
|
|
4794
|
+
className: cn(
|
|
4795
|
+
"truncate",
|
|
4796
|
+
CHAT_ATTACHED_FILE_CONFIG.nameTypo,
|
|
4797
|
+
CHAT_ATTACHED_FILE_CONFIG.textColor
|
|
4798
|
+
),
|
|
4799
|
+
children: name
|
|
4800
|
+
}
|
|
4801
|
+
)
|
|
4817
4802
|
] }),
|
|
4818
|
-
|
|
4819
|
-
|
|
4803
|
+
onRemove != null && /* @__PURE__ */ jsx(
|
|
4804
|
+
SGhostButton,
|
|
4820
4805
|
{
|
|
4821
|
-
|
|
4822
|
-
"
|
|
4806
|
+
icon: "close",
|
|
4807
|
+
size: "xs",
|
|
4808
|
+
ariaLabel: `${name} \uC81C\uAC70`,
|
|
4823
4809
|
onClick: onRemove,
|
|
4824
|
-
className: "
|
|
4825
|
-
children: /* @__PURE__ */ jsx(SIcon, { name: "close", size: 16 })
|
|
4810
|
+
className: "shrink-0"
|
|
4826
4811
|
}
|
|
4827
4812
|
)
|
|
4828
4813
|
]
|
|
@@ -4831,12 +4816,120 @@ var SChatAttachedFileImpl = /* @__PURE__ */ forwardRef(
|
|
|
4831
4816
|
}
|
|
4832
4817
|
);
|
|
4833
4818
|
var SChatAttachedFile2 = /* @__PURE__ */ withDisplayName(SChatAttachedFileImpl, "SChatAttachedFile");
|
|
4819
|
+
|
|
4820
|
+
// src/components/SChatFile/chatFile.config.ts
|
|
4821
|
+
var CHAT_FILE_CONFIG = {
|
|
4822
|
+
/** 바깥 상자 — 좌우 비대칭 여백·상하 여백·면·선·라운드 */
|
|
4823
|
+
box: "pl-(--cmp-chatFile-paddingLeft) pr-(--cmp-chatFile-paddingRight) py-(--cmp-chatFile-paddingY) rounded-(--cmp-chatFile-radius) border-(length:--cmp-chatFile-border-width) border-(--cmp-chatFile-border-color) bg-(--cmp-chatFile-bg-default)",
|
|
4824
|
+
/** 아이콘 칩 ↔ 파일명 간격 */
|
|
4825
|
+
contentGap: "gap-(--cmp-chatFile-gap)",
|
|
4826
|
+
/**
|
|
4827
|
+
* 클립 아이콘이 앉는 칩 — 내부 패딩·면·라운드 (높이는 아이콘+패딩이 정한다).
|
|
4828
|
+
*
|
|
4829
|
+
* hover 면은 상자가 아니라 여기에도 걸린다 — 카드가 어두워질 때 칩은 반대로 밝아져
|
|
4830
|
+
* 칩이 배경에 묻히지 않는다. 상자의 hover 를 따라가야 하므로 group-hover 로 받는다.
|
|
4831
|
+
*
|
|
4832
|
+
* **`--sys-color-bg-frame` 은 임시다.** 시안의 hover 칩 색(`#FFFFFF`)에 대응하는
|
|
4833
|
+
* 컴포넌트 토큰이 없다 — `component.chatMessage.chatFile.icon.wrapper.bg` 는 값이 하나뿐이라
|
|
4834
|
+
* hover 를 담지 못한다. 상자의 `bg.default` 가 참조하는 것과 같은 시스템 토큰으로 대신 걸어 둔다.
|
|
4835
|
+
* 디자인팀에 `icon.wrapper.bg.hover` 를 요청해 나오는 대로 갈아끼운다.
|
|
4836
|
+
*/
|
|
4837
|
+
tile: "p-(--cmp-chatFile-icon-wrapper-padding) rounded-(--cmp-chatFile-icon-wrapper-radius) bg-(--cmp-chatFile-icon-wrapper-bg) group-hover/chat-file:bg-(--sys-color-bg-frame)",
|
|
4838
|
+
/** 칩 안 클립 아이콘 크기(px). SVG width/height 는 속성값이라 CSS 변수를 못 받아 값으로 둔다 (토큰 값과 동일) */
|
|
4839
|
+
iconSize: 20,
|
|
4840
|
+
/** 파일명 타이포 프리셋 */
|
|
4841
|
+
nameTypo: "typo-body-md-bold",
|
|
4842
|
+
/** 파일명·부가정보 텍스트색 */
|
|
4843
|
+
textColor: "text-(--cmp-chatFile-text-color)",
|
|
4844
|
+
/**
|
|
4845
|
+
* 눌리는 모양 — 커서·hover 면·포커스 링. 보낸 파일은 언제나 열 수 있으므로 늘 붙는다
|
|
4846
|
+
* (`onFileClick` 이 필수다).
|
|
4847
|
+
*
|
|
4848
|
+
* 상자의 여백·면·선과 따로 두는 이유: 이건 상호작용을 드러내는 값이라 성격이 다르다.
|
|
4849
|
+
* 링은 `SExpansionItem`·`SStepper` 와 같은 accent outline.
|
|
4850
|
+
*/
|
|
4851
|
+
interactive: "cursor-pointer hover:bg-(--cmp-chatFile-bg-hover) focus-visible:outline focus-visible:outline-2 focus-visible:outline-(--sys-color-border-accent)"
|
|
4852
|
+
};
|
|
4853
|
+
var SIZE_UNITS = ["B", "KB", "MB", "GB", "TB"];
|
|
4854
|
+
function formatFileSize(bytes) {
|
|
4855
|
+
if (!Number.isFinite(bytes) || bytes < 0) return "";
|
|
4856
|
+
let value = bytes;
|
|
4857
|
+
let unit = 0;
|
|
4858
|
+
while (value >= 1024 && unit < SIZE_UNITS.length - 1) {
|
|
4859
|
+
value /= 1024;
|
|
4860
|
+
unit += 1;
|
|
4861
|
+
}
|
|
4862
|
+
return unit === 0 ? `${Math.round(value)}${SIZE_UNITS[0]}` : `${value.toFixed(1)}${SIZE_UNITS[unit]}`;
|
|
4863
|
+
}
|
|
4864
|
+
function normalizeExtension(extension) {
|
|
4865
|
+
return extension.trim().replace(/^\.+/, "").toLowerCase();
|
|
4866
|
+
}
|
|
4867
|
+
function fileExtension(name) {
|
|
4868
|
+
const dot = name.lastIndexOf(".");
|
|
4869
|
+
return dot > 0 && dot < name.length - 1 ? name.slice(dot + 1).toLowerCase() : "";
|
|
4870
|
+
}
|
|
4871
|
+
var SChatFileImpl = /* @__PURE__ */ forwardRef(function SChatFile({ name, extension, size, onFileClick, className, ...rest }, ref) {
|
|
4872
|
+
const ext = extension != null ? normalizeExtension(extension) : fileExtension(name);
|
|
4873
|
+
const meta = [ext, size != null ? formatFileSize(size) : ""].filter(Boolean).join(" / ");
|
|
4874
|
+
return /* @__PURE__ */ jsxs(
|
|
4875
|
+
"div",
|
|
4876
|
+
{
|
|
4877
|
+
ref,
|
|
4878
|
+
className: cn(
|
|
4879
|
+
// group/chat-file: 아이콘 칩이 상자의 hover 를 따라가야 해서 이름을 붙인다 (config.tile 참고)
|
|
4880
|
+
"group/chat-file inline-flex max-w-full items-center border-solid",
|
|
4881
|
+
CHAT_FILE_CONFIG.box,
|
|
4882
|
+
CHAT_FILE_CONFIG.contentGap,
|
|
4883
|
+
CHAT_FILE_CONFIG.interactive,
|
|
4884
|
+
className
|
|
4885
|
+
),
|
|
4886
|
+
...rest,
|
|
4887
|
+
role: "button",
|
|
4888
|
+
tabIndex: 0,
|
|
4889
|
+
onClick: (event) => {
|
|
4890
|
+
rest.onClick?.(event);
|
|
4891
|
+
onFileClick?.();
|
|
4892
|
+
},
|
|
4893
|
+
onKeyDown: (event) => {
|
|
4894
|
+
rest.onKeyDown?.(event);
|
|
4895
|
+
if (event.key !== "Enter" && event.key !== " ") return;
|
|
4896
|
+
event.preventDefault();
|
|
4897
|
+
onFileClick?.();
|
|
4898
|
+
},
|
|
4899
|
+
children: [
|
|
4900
|
+
/* @__PURE__ */ jsx(
|
|
4901
|
+
"span",
|
|
4902
|
+
{
|
|
4903
|
+
className: cn(
|
|
4904
|
+
"inline-grid shrink-0 place-items-center text-(--sys-color-fg-accentLight)",
|
|
4905
|
+
CHAT_FILE_CONFIG.tile
|
|
4906
|
+
),
|
|
4907
|
+
children: /* @__PURE__ */ jsx(SIcon, { name: "attachFile", size: CHAT_FILE_CONFIG.iconSize })
|
|
4908
|
+
}
|
|
4909
|
+
),
|
|
4910
|
+
/* @__PURE__ */ jsxs("span", { className: "flex min-w-0 flex-col", children: [
|
|
4911
|
+
/* @__PURE__ */ jsx("span", { className: cn("truncate", CHAT_FILE_CONFIG.nameTypo, CHAT_FILE_CONFIG.textColor), children: name }),
|
|
4912
|
+
meta !== "" && /* @__PURE__ */ jsx("span", { className: cn("typo-body-xs-default truncate", CHAT_FILE_CONFIG.textColor), children: meta })
|
|
4913
|
+
] })
|
|
4914
|
+
]
|
|
4915
|
+
}
|
|
4916
|
+
);
|
|
4917
|
+
});
|
|
4918
|
+
var SChatFile2 = /* @__PURE__ */ withDisplayName(SChatFileImpl, "SChatFile");
|
|
4834
4919
|
var CHAT_MESSAGE_DIRECTIONS = ["incoming", "outgoing"];
|
|
4835
|
-
var CHAT_MESSAGE_VARIANTS = ["default", "template"];
|
|
4920
|
+
var CHAT_MESSAGE_VARIANTS = ["default", "template", "admin"];
|
|
4836
4921
|
var BUBBLE_SURFACE = {
|
|
4837
|
-
incoming: "bg-(--
|
|
4838
|
-
outgoing: "bg-(--
|
|
4839
|
-
template: "bg-(--
|
|
4922
|
+
incoming: "bg-(--cmp-chatBubble-incoming-bg) text-(--cmp-chatBubble-incoming-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"
|
|
4840
4933
|
};
|
|
4841
4934
|
var CHAT_MESSAGE_READ_STATUSES = ["read", "unread"];
|
|
4842
4935
|
var READ_STATUS_LABEL = {
|
|
@@ -4844,8 +4937,8 @@ var READ_STATUS_LABEL = {
|
|
|
4844
4937
|
unread: "\uC548 \uC77D\uC74C"
|
|
4845
4938
|
};
|
|
4846
4939
|
var READ_STATUS_COLOR = {
|
|
4847
|
-
read: "text-(--
|
|
4848
|
-
unread: "text-(--
|
|
4940
|
+
read: "text-(--cmp-chatTimeStamp-color)",
|
|
4941
|
+
unread: "text-(--cmp-chatReadMark-color)"
|
|
4849
4942
|
};
|
|
4850
4943
|
var AVATAR_SIZE = 32;
|
|
4851
4944
|
var AVATAR_ROW_HEIGHT = 40;
|
|
@@ -4857,7 +4950,7 @@ function Avatar({ src, alt, placeholder }) {
|
|
|
4857
4950
|
return /* @__PURE__ */ jsx("div", { className: "flex shrink-0 items-center", style: { height: AVATAR_ROW_HEIGHT }, children: /* @__PURE__ */ jsx(
|
|
4858
4951
|
"div",
|
|
4859
4952
|
{
|
|
4860
|
-
className: "relative overflow-hidden rounded-
|
|
4953
|
+
className: "relative overflow-hidden rounded-(--cmp-chatAvatar-radius) bg-(--cmp-chatAvatar-bg)",
|
|
4861
4954
|
style: { width: AVATAR_SIZE, height: AVATAR_SIZE },
|
|
4862
4955
|
children: src != null ? /* @__PURE__ */ jsx(SImage, { src, alt: alt ?? "", fit: "cover", className: "h-full w-full" }) : (
|
|
4863
4956
|
// 원 밖으로 나가는 만큼을 좌우로 나눠 가운데를 맞추고, 아래는 원이 자른다.
|
|
@@ -4866,7 +4959,7 @@ function Avatar({ src, alt, placeholder }) {
|
|
|
4866
4959
|
{
|
|
4867
4960
|
name: "user",
|
|
4868
4961
|
size: AVATAR_ICON_SIZE,
|
|
4869
|
-
color: "var(--
|
|
4962
|
+
color: "var(--cmp-chatAvatar-icon)",
|
|
4870
4963
|
className: "absolute top-0",
|
|
4871
4964
|
style: { left: (AVATAR_SIZE - AVATAR_ICON_SIZE) / 2 }
|
|
4872
4965
|
}
|
|
@@ -4886,12 +4979,14 @@ var SChatMessageImpl = /* @__PURE__ */ forwardRef(function SChatMessage({
|
|
|
4886
4979
|
variant = "default",
|
|
4887
4980
|
actions,
|
|
4888
4981
|
messages,
|
|
4982
|
+
attachments,
|
|
4889
4983
|
className,
|
|
4890
4984
|
...rest
|
|
4891
4985
|
}, ref) {
|
|
4892
4986
|
const outgoing = direction === "outgoing";
|
|
4893
|
-
const surface =
|
|
4987
|
+
const surface = outgoing ? OUTGOING_SURFACE[variant] : "incoming";
|
|
4894
4988
|
const bubbles = messages ?? [];
|
|
4989
|
+
const files = attachments ?? [];
|
|
4895
4990
|
return /* @__PURE__ */ jsxs(
|
|
4896
4991
|
"div",
|
|
4897
4992
|
{
|
|
@@ -4899,45 +4994,77 @@ var SChatMessageImpl = /* @__PURE__ */ forwardRef(function SChatMessage({
|
|
|
4899
4994
|
className: cn(
|
|
4900
4995
|
// 아바타와 본문 열이 같은 줄에 서고, 보낸 메시지는 좌우를 뒤집는다.
|
|
4901
4996
|
// items-start 라 이름이 있든 없든 아바타는 언제나 첫 줄에 붙는다.
|
|
4902
|
-
"flex items-start gap-
|
|
4997
|
+
"flex items-start gap-(--cmp-chatMessage-avatarGap)",
|
|
4903
4998
|
outgoing && "flex-row-reverse",
|
|
4904
4999
|
className
|
|
4905
5000
|
),
|
|
4906
5001
|
...rest,
|
|
4907
5002
|
children: [
|
|
4908
5003
|
!outgoing && /* @__PURE__ */ jsx(Avatar, { src: avatarSrc, alt: avatarAlt, placeholder: continued }),
|
|
4909
|
-
/* @__PURE__ */ jsxs(
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
|
|
4914
|
-
|
|
4915
|
-
|
|
4916
|
-
|
|
5004
|
+
/* @__PURE__ */ jsxs(
|
|
5005
|
+
"div",
|
|
5006
|
+
{
|
|
5007
|
+
className: cn(
|
|
5008
|
+
"flex min-w-0 flex-col gap-(--cmp-chatMessage-contents-gap)",
|
|
5009
|
+
outgoing ? "items-end" : "items-start"
|
|
5010
|
+
),
|
|
5011
|
+
children: [
|
|
5012
|
+
!outgoing && !continued && sender != null && /* @__PURE__ */ jsx("span", { className: "typo-body-sm-bold px-(--cmp-chatMessage-name-paddingX) text-(--cmp-chatMessage-name-color)", children: sender }),
|
|
5013
|
+
bubbles.length > 0 && // 말풍선끼리는 이름·시각과의 간격(4px)보다 벌려 한 덩어리 안의 여러 발화로 읽히게 한다.
|
|
5014
|
+
/* @__PURE__ */ jsx(
|
|
4917
5015
|
"div",
|
|
4918
5016
|
{
|
|
4919
5017
|
className: cn(
|
|
4920
|
-
|
|
4921
|
-
|
|
4922
|
-
// 한글은 기본 규칙으로 두면 어절 한가운데서도 줄이 바뀐다 — 띄어쓰기에서만 끊고,
|
|
4923
|
-
// 끊을 자리가 없는 긴 토큰(URL·주문번호)만 흘려보낸다.
|
|
4924
|
-
"break-keep wrap-break-word",
|
|
4925
|
-
BUBBLE_SURFACE[surface]
|
|
5018
|
+
"flex min-w-0 flex-col gap-(--cmp-chatMessage-group-bubble-gap)",
|
|
5019
|
+
outgoing ? "items-end" : "items-start"
|
|
4926
5020
|
),
|
|
4927
|
-
children: message
|
|
4928
|
-
|
|
4929
|
-
|
|
4930
|
-
|
|
4931
|
-
|
|
4932
|
-
|
|
4933
|
-
|
|
4934
|
-
|
|
4935
|
-
|
|
4936
|
-
|
|
4937
|
-
|
|
4938
|
-
|
|
4939
|
-
|
|
4940
|
-
|
|
5021
|
+
children: bubbles.map((message, index) => /* @__PURE__ */ jsx(
|
|
5022
|
+
"div",
|
|
5023
|
+
{
|
|
5024
|
+
className: cn(
|
|
5025
|
+
"typo-body-md-medium max-w-full rounded-(--cmp-chatBubble-radius) px-(--cmp-chatBubble-paddingX) py-(--cmp-chatBubble-paddingY)",
|
|
5026
|
+
// 한글은 기본 규칙으로 두면 어절 한가운데서도 줄이 바뀐다 — 띄어쓰기에서만 끊고,
|
|
5027
|
+
// 끊을 자리가 없는 긴 토큰(URL·주문번호)만 흘려보낸다.
|
|
5028
|
+
"break-keep wrap-break-word",
|
|
5029
|
+
BUBBLE_SURFACE[surface]
|
|
5030
|
+
),
|
|
5031
|
+
children: message
|
|
5032
|
+
},
|
|
5033
|
+
index
|
|
5034
|
+
))
|
|
5035
|
+
}
|
|
5036
|
+
),
|
|
5037
|
+
files.length > 0 && // 파일 칸끼리는 말풍선과 같은 간격으로 벌린다 — 한 덩어리 안의 여러 발화로 읽힌다.
|
|
5038
|
+
// 말풍선과 달리 면으로 감싸지 않는다 (`attachments` 주석 참고).
|
|
5039
|
+
/* @__PURE__ */ jsx(
|
|
5040
|
+
"div",
|
|
5041
|
+
{
|
|
5042
|
+
className: cn(
|
|
5043
|
+
"flex min-w-0 flex-col gap-(--cmp-chatMessage-group-bubble-gap)",
|
|
5044
|
+
outgoing ? "items-end" : "items-start"
|
|
5045
|
+
),
|
|
5046
|
+
children: files.map((file, index) => /* @__PURE__ */ jsx("div", { className: "max-w-full", children: file }, index))
|
|
5047
|
+
}
|
|
5048
|
+
),
|
|
5049
|
+
(readStatus != null || time != null) && // 읽음 표시와 시각은 한 줄에 선다 — 읽음이 먼저, 시각이 뒤다.
|
|
5050
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-(--cmp-chatMessage-subData-gap) px-(--cmp-chatMessage-subData-paddingX)", children: [
|
|
5051
|
+
readStatus != null && /* @__PURE__ */ jsx("span", { className: cn("typo-body-sm-bold", READ_STATUS_COLOR[readStatus]), children: READ_STATUS_LABEL[readStatus] }),
|
|
5052
|
+
time != null && /* @__PURE__ */ jsx("span", { className: "typo-body-sm-medium text-(--cmp-chatTimeStamp-color)", children: time })
|
|
5053
|
+
] }),
|
|
5054
|
+
actions != null && // 버튼이 많아 한 줄에 안 들어가면 감싼다 — 넘쳐서 잘리는 것보다 낫다.
|
|
5055
|
+
/* @__PURE__ */ jsx(
|
|
5056
|
+
"div",
|
|
5057
|
+
{
|
|
5058
|
+
className: cn(
|
|
5059
|
+
"flex flex-wrap gap-sd-8 px-(--cmp-chatMessage-bottomButtons-paddingX)",
|
|
5060
|
+
outgoing && "justify-end"
|
|
5061
|
+
),
|
|
5062
|
+
children: actions
|
|
5063
|
+
}
|
|
5064
|
+
)
|
|
5065
|
+
]
|
|
5066
|
+
}
|
|
5067
|
+
)
|
|
4941
5068
|
]
|
|
4942
5069
|
}
|
|
4943
5070
|
);
|
|
@@ -4953,14 +5080,13 @@ var SChatSystemMessageImpl = /* @__PURE__ */ forwardRef(
|
|
|
4953
5080
|
"span",
|
|
4954
5081
|
{
|
|
4955
5082
|
className: cn(
|
|
4956
|
-
//
|
|
4957
|
-
//
|
|
4958
|
-
//
|
|
4959
|
-
|
|
4960
|
-
"
|
|
4961
|
-
"
|
|
4962
|
-
"
|
|
4963
|
-
"px-sd-16 py-sd-4 text-center",
|
|
5083
|
+
// 라운드는 말풍선과 같은 토큰을 쓴다 — 여러 줄이 돼도 모서리가 상자 높이를 따라
|
|
5084
|
+
// 부풀지 않는다. 제 몫인 chatSystemMessage.radius 는 pill(9999px)이라 쓰지 않는다.
|
|
5085
|
+
// 두 줄부터 좌우가 커다란 반원이 되어 첫 글자가 안으로 밀리기 때문이다.
|
|
5086
|
+
"inline-flex max-w-full rounded-(--cmp-chatBubble-radius) border",
|
|
5087
|
+
"border-(--cmp-chatSystemMessage-border-color) bg-(--cmp-chatSystemMessage-bg)",
|
|
5088
|
+
"typo-body-sm-medium text-(--cmp-chatSystemMessage-color)",
|
|
5089
|
+
"px-(--cmp-chatSystemMessage-paddingX) py-(--cmp-chatSystemMessage-paddingY) text-center",
|
|
4964
5090
|
// 한글은 기본 규칙으로 두면 어절 한가운데서도 줄이 바뀐다 — 띄어쓰기에서만 끊고,
|
|
4965
5091
|
// 끊을 자리가 없는 긴 토큰(URL·주문번호)만 흘려보낸다 (SChatMessage 말풍선과 같은 규칙).
|
|
4966
5092
|
// 가운데 정렬이라 마지막 줄에 한 단어만 남으면 특히 눈에 띄므로 text-pretty 로 줄을 고른다.
|
|
@@ -6285,10 +6411,21 @@ function SCheckbox({
|
|
|
6285
6411
|
) })
|
|
6286
6412
|
}
|
|
6287
6413
|
),
|
|
6288
|
-
label && // 고스트(500) 텍스트로 너비를 고정 → 두께 400↔500 전환 시 레이아웃이 밀리지
|
|
6289
|
-
|
|
6290
|
-
|
|
6291
|
-
|
|
6414
|
+
label && // 고스트(500) 텍스트로 너비를 고정 → 두께 400↔500 전환 시 레이아웃이 밀리지 않음.
|
|
6415
|
+
// 한글은 기본 규칙으로 두면 어절 한가운데서도 줄이 바뀐다. 그러면 min-content 가 한 글자가
|
|
6416
|
+
// 되어, flex 아이템을 지켜 주는 min-width:auto 가 무력해지고 폭이 남는데도 라벨부터
|
|
6417
|
+
// 찌그러진다 — 띄어쓰기에서만 끊고, 끊을 자리가 없는 긴 토큰만 흘려보낸다
|
|
6418
|
+
// (SRadio·SSwitch 라벨과 같은 규칙). 두 사본에 함께 걸려야 하므로 바깥 grid 에 준다.
|
|
6419
|
+
/* @__PURE__ */ jsxs("span", { className: "grid break-keep wrap-break-word", style: { color: labelColor }, children: [
|
|
6420
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "typo-body-sm-medium invisible", style: { gridArea: "1 / 1" }, children: label }),
|
|
6421
|
+
/* @__PURE__ */ jsx(
|
|
6422
|
+
"span",
|
|
6423
|
+
{
|
|
6424
|
+
className: isChecked ? "typo-body-sm-medium" : "typo-body-sm-default",
|
|
6425
|
+
style: { gridArea: "1 / 1" },
|
|
6426
|
+
children: label
|
|
6427
|
+
}
|
|
6428
|
+
)
|
|
6292
6429
|
] })
|
|
6293
6430
|
]
|
|
6294
6431
|
}
|
|
@@ -7742,7 +7879,7 @@ function SSwitch({
|
|
|
7742
7879
|
style
|
|
7743
7880
|
}) {
|
|
7744
7881
|
const right = rightLabel;
|
|
7745
|
-
const renderLabel = (text) => text ? /* @__PURE__ */ jsx("span", { className: "
|
|
7882
|
+
const renderLabel = (text) => text ? /* @__PURE__ */ jsx("span", { className: "typo-body-sm-medium break-keep wrap-break-word", children: text }) : null;
|
|
7746
7883
|
const trackBg = disabled ? value ? "var(--cmp-switch-bg-disabled-on)" : "var(--cmp-switch-bg-disabled-off)" : value ? "var(--cmp-switch-bg-on)" : "var(--cmp-switch-bg-off)";
|
|
7747
7884
|
return /* @__PURE__ */ jsxs(
|
|
7748
7885
|
"label",
|
|
@@ -11749,7 +11886,7 @@ var SPageHeader = /* @__PURE__ */ withDisplayName(
|
|
|
11749
11886
|
// plain 에는 전용 배경 토큰이 없어 시스템 흰 표면을 쓴다(토큰이 생기면 그것으로 갈아끼운다).
|
|
11750
11887
|
bar ? "h-(--cmp-pageHeader-bar-height) border-b border-(--cmp-pageHeader-bar-border) bg-(--cmp-pageHeader-bar-bg) py-(--cmp-pageHeader-bar-paddingY)" : cn(
|
|
11751
11888
|
// 스크롤되면 그림자를 더해 콘텐츠 위에 뜬 것처럼 보이게 한다.
|
|
11752
|
-
"bg-(--sys-color-bg-frame)
|
|
11889
|
+
"bg-(--sys-color-bg-frame) py-(--cmp-pageHeader-plain-paddingY)",
|
|
11753
11890
|
isScrolled && "shadow-floating"
|
|
11754
11891
|
),
|
|
11755
11892
|
className
|
|
@@ -11759,7 +11896,10 @@ var SPageHeader = /* @__PURE__ */ withDisplayName(
|
|
|
11759
11896
|
children: /* @__PURE__ */ jsxs(
|
|
11760
11897
|
"div",
|
|
11761
11898
|
{
|
|
11762
|
-
className:
|
|
11899
|
+
className: cn(
|
|
11900
|
+
"flex min-w-0 flex-1 gap-(--cmp-pageHeader-gap)",
|
|
11901
|
+
bar ? "items-center" : "items-start"
|
|
11902
|
+
),
|
|
11763
11903
|
style: rowStyle,
|
|
11764
11904
|
children: [
|
|
11765
11905
|
hasBack && /* @__PURE__ */ jsx(
|
|
@@ -11769,7 +11909,7 @@ var SPageHeader = /* @__PURE__ */ withDisplayName(
|
|
|
11769
11909
|
size: "xs",
|
|
11770
11910
|
ariaLabel: "\uB4A4\uB85C\uAC00\uAE30",
|
|
11771
11911
|
onClick: onBack,
|
|
11772
|
-
className: "shrink-0"
|
|
11912
|
+
className: cn("shrink-0", bar ? "" : "mt-[3px]")
|
|
11773
11913
|
}
|
|
11774
11914
|
),
|
|
11775
11915
|
/* @__PURE__ */ jsxs(
|
|
@@ -15902,7 +16042,7 @@ function SRadio({
|
|
|
15902
16042
|
label && /* @__PURE__ */ jsxs(
|
|
15903
16043
|
"span",
|
|
15904
16044
|
{
|
|
15905
|
-
className: "grid",
|
|
16045
|
+
className: "grid break-keep wrap-break-word",
|
|
15906
16046
|
style: {
|
|
15907
16047
|
color: disabled ? "var(--cmp-radio-label-disabled)" : "var(--cmp-radio-label-default)"
|
|
15908
16048
|
},
|
|
@@ -17717,6 +17857,673 @@ function SKeyValueTable({
|
|
|
17717
17857
|
)
|
|
17718
17858
|
] });
|
|
17719
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
|
+
});
|
|
17720
18527
|
var BUTTON_STYLE = {
|
|
17721
18528
|
default: { color: "secondary" },
|
|
17722
18529
|
danger: { color: "danger", outline: true },
|
|
@@ -22615,15 +23422,15 @@ var SBarcodeInput = /* @__PURE__ */ forwardRef(
|
|
|
22615
23422
|
);
|
|
22616
23423
|
|
|
22617
23424
|
// src/components/STimePicker/timepicker.config.ts
|
|
22618
|
-
var
|
|
23425
|
+
var V4 = (path) => `var(--cmp-timepicker-${path})`;
|
|
22619
23426
|
var TIMEPICKER_SIZES = ["sm", "md"];
|
|
22620
23427
|
var TIMEPICKER_SIZE_CONFIG = {
|
|
22621
23428
|
sm: {
|
|
22622
|
-
height:
|
|
22623
|
-
paddingX:
|
|
22624
|
-
gap:
|
|
22625
|
-
icon:
|
|
22626
|
-
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"),
|
|
22627
23434
|
// TODO: component.timepicker 토큰에 typography가 추가되면 CSS 변수로 교체한다.
|
|
22628
23435
|
fontSize: 12,
|
|
22629
23436
|
lineHeight: 20,
|
|
@@ -22632,11 +23439,11 @@ var TIMEPICKER_SIZE_CONFIG = {
|
|
|
22632
23439
|
fieldMaxWidth: "md"
|
|
22633
23440
|
},
|
|
22634
23441
|
md: {
|
|
22635
|
-
height:
|
|
22636
|
-
paddingX:
|
|
22637
|
-
gap:
|
|
22638
|
-
icon:
|
|
22639
|
-
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"),
|
|
22640
23447
|
// TODO: component.timepicker 토큰에 typography가 추가되면 CSS 변수로 교체한다.
|
|
22641
23448
|
fontSize: 14,
|
|
22642
23449
|
lineHeight: 24,
|
|
@@ -23526,6 +24333,6 @@ function STimeRangePicker({
|
|
|
23526
24333
|
);
|
|
23527
24334
|
}
|
|
23528
24335
|
|
|
23529
|
-
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,
|
|
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 };
|
|
23530
24337
|
//# sourceMappingURL=index.js.map
|
|
23531
24338
|
//# sourceMappingURL=index.js.map
|