@wipco/sdui 1.5.0 → 1.6.1

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.
Files changed (143) hide show
  1. package/dist/{DatePicker-C_TT_SOw.d.ts → DatePicker-DX9juElE.d.ts} +10 -1
  2. package/dist/{chunk-7P7N43JV.js → chunk-34BABTH4.js} +2 -2
  3. package/dist/{chunk-DMLDKW7K.js → chunk-37BNG4DT.js} +1 -1
  4. package/dist/{chunk-5WW4MBRO.js → chunk-3JTBOABA.js} +6 -2
  5. package/dist/{chunk-HBIIEYKW.js → chunk-3PCR73OC.js} +4 -4
  6. package/dist/{chunk-VY7MVBSE.js → chunk-3PT5OBOU.js} +2 -2
  7. package/dist/{chunk-YVTV4ZV6.js → chunk-4YPPL7JK.js} +1 -1
  8. package/dist/{chunk-E6ISNQK3.js → chunk-6YBSBC77.js} +31 -8
  9. package/dist/{chunk-YWUDBLO6.js → chunk-7G6J6QFC.js} +2 -2
  10. package/dist/{chunk-UV6HHFEW.js → chunk-A57GKV5F.js} +129 -68
  11. package/dist/{chunk-I45EUZC6.js → chunk-AJ3KDBEE.js} +4 -2
  12. package/dist/chunk-BITS7UJY.js +333 -0
  13. package/dist/{chunk-DOL57OOP.js → chunk-BV37UCDF.js} +3 -3
  14. package/dist/chunk-CSZCWBOE.js +256 -0
  15. package/dist/{chunk-XGFDY43I.js → chunk-CUFGH3LB.js} +88 -18
  16. package/dist/{chunk-A6LAIGUS.js → chunk-EGFDELSF.js} +7 -4
  17. package/dist/{chunk-JIV4YHCZ.js → chunk-EJIWOD7W.js} +1 -1
  18. package/dist/{chunk-CXW6KT6X.js → chunk-ESAFQXEU.js} +3 -3
  19. package/dist/{chunk-N5RKM6IC.js → chunk-EUSC5NMZ.js} +4 -4
  20. package/dist/{chunk-RWSH2MBU.js → chunk-FP32LNFK.js} +73 -19
  21. package/dist/{chunk-44TDRCKR.js → chunk-GKYUDTMI.js} +5 -5
  22. package/dist/chunk-GSM664TX.js +38 -0
  23. package/dist/{chunk-I5SD7XRA.js → chunk-GSXD52XG.js} +3 -3
  24. package/dist/{chunk-O4IISNE7.js → chunk-IB2ZPUTF.js} +51 -12
  25. package/dist/{chunk-YOZN35HD.js → chunk-IIRVHSNK.js} +3 -3
  26. package/dist/{chunk-FNMS7OB7.js → chunk-J7BZF4T4.js} +5 -5
  27. package/dist/{chunk-KO6OEY3G.js → chunk-JHFA5RDN.js} +3 -13
  28. package/dist/{chunk-DKVSM5HX.js → chunk-KDKRFWIF.js} +6 -6
  29. package/dist/chunk-KO3EUNWB.js +380 -0
  30. package/dist/{chunk-JYAR7D5U.js → chunk-NLSR3JYU.js} +2 -2
  31. package/dist/{chunk-ZORQPB6B.js → chunk-O4EMQWCV.js} +325 -360
  32. package/dist/{chunk-2HLXCKK7.js → chunk-OCYJ2ZOV.js} +2 -2
  33. package/dist/{chunk-OQ52XMGG.js → chunk-OKNF2SBB.js} +4 -4
  34. package/dist/{chunk-GNA6SX2H.js → chunk-OQ7A3SFM.js} +2 -2
  35. package/dist/{chunk-A5XQOEVH.js → chunk-PCFH74RD.js} +4 -4
  36. package/dist/{chunk-BEM2YS7U.js → chunk-SGGIWPP6.js} +3 -3
  37. package/dist/{chunk-NIJUVTEW.js → chunk-TB7G5DXQ.js} +3 -3
  38. package/dist/{chunk-GZBY7I4Y.js → chunk-TWUKNUUQ.js} +66 -10
  39. package/dist/{chunk-WPSK2CB5.js → chunk-UC725BGW.js} +16 -5
  40. package/dist/{chunk-ZFNFPXPR.js → chunk-US3K6Q5T.js} +2 -2
  41. package/dist/{chunk-MPT7WJG2.js → chunk-USNPJEXH.js} +5 -5
  42. package/dist/{chunk-SAPBDJJQ.js → chunk-UZZLMPRW.js} +2 -2
  43. package/dist/{chunk-Q4SWUINA.js → chunk-VAXRTBJH.js} +1 -1
  44. package/dist/{chunk-RYBBPFI7.js → chunk-VQKX5PJ4.js} +4 -4
  45. package/dist/chunk-VR5MFUD2.js +106 -0
  46. package/dist/{chunk-JZXU22PE.js → chunk-VSNHDHGS.js} +208 -117
  47. package/dist/{chunk-2YP5N4VV.js → chunk-VZOQRQAR.js} +1 -1
  48. package/dist/{chunk-AXGF4M57.js → chunk-XAXROQKB.js} +1 -1
  49. package/dist/{chunk-IZ3JA2BR.js → chunk-XFKNU6QN.js} +3 -3
  50. package/dist/{chunk-GTGS2QRL.js → chunk-XUVWIRZJ.js} +3 -3
  51. package/dist/{chunk-RNOUXUBO.js → chunk-YYAGE733.js} +323 -59
  52. package/dist/index.d.ts +449 -91
  53. package/dist/index.js +1060 -138
  54. package/dist/{layout-C3nRXMwb.d.ts → layout-DmRikwZb.d.ts} +5 -4
  55. package/dist/meta.d.ts +1 -1
  56. package/dist/meta.js +351 -25
  57. package/dist/rich/ExcelUploadCard.d.ts +1 -1
  58. package/dist/rich/ExcelUploadCard.js +5 -4
  59. package/dist/rich.d.ts +22 -3
  60. package/dist/rich.js +304 -35
  61. package/dist/styles.css +119 -4
  62. package/dist/{types-COjGKgmd.d.ts → types-CMikpoN5.d.ts} +65 -2
  63. package/dist/widgets/ApprovalLineEditor.d.ts +1 -1
  64. package/dist/widgets/ApprovalLineEditor.js +9 -8
  65. package/dist/widgets/ApprovalOverridesEditor.d.ts +1 -1
  66. package/dist/widgets/ApprovalOverridesEditor.js +11 -10
  67. package/dist/widgets/ApprovalStepper.d.ts +29 -4
  68. package/dist/widgets/ApprovalStepper.js +3 -3
  69. package/dist/widgets/AutocompleteField.d.ts +1 -1
  70. package/dist/widgets/AutocompleteField.js +9 -8
  71. package/dist/widgets/ColumnBoard.d.ts +1 -1
  72. package/dist/widgets/ColumnBoard.js +3 -3
  73. package/dist/widgets/DiffView.d.ts +1 -1
  74. package/dist/widgets/DiffView.js +3 -3
  75. package/dist/widgets/Disclosure.d.ts +1 -1
  76. package/dist/widgets/Disclosure.js +3 -3
  77. package/dist/widgets/EventCalendar.d.ts +5 -1
  78. package/dist/widgets/EventCalendar.js +5 -3
  79. package/dist/widgets/FileDropzone.d.ts +1 -1
  80. package/dist/widgets/FileDropzone.js +9 -7
  81. package/dist/widgets/ImageLightbox.d.ts +1 -1
  82. package/dist/widgets/ImageLightbox.js +7 -5
  83. package/dist/widgets/InlineEdit.d.ts +1 -1
  84. package/dist/widgets/InlineEdit.js +3 -3
  85. package/dist/widgets/JsonField.d.ts +1 -1
  86. package/dist/widgets/JsonField.js +3 -3
  87. package/dist/widgets/LiveClock.d.ts +1 -1
  88. package/dist/widgets/LiveClock.js +2 -2
  89. package/dist/widgets/OptionSchemaEditor.d.ts +1 -1
  90. package/dist/widgets/OptionSchemaEditor.js +4 -4
  91. package/dist/widgets/OptionValueFields.d.ts +1 -1
  92. package/dist/widgets/OptionValueFields.js +4 -4
  93. package/dist/widgets/OrgChart.d.ts +1 -1
  94. package/dist/widgets/OrgChart.js +4 -4
  95. package/dist/widgets/OrgChartBoard.d.ts +15 -33
  96. package/dist/widgets/OrgChartBoard.js +10 -8
  97. package/dist/widgets/PeriodFilter.d.ts +1 -1
  98. package/dist/widgets/PeriodFilter.js +6 -5
  99. package/dist/widgets/PermissionMatrixEditor.d.ts +34 -2
  100. package/dist/widgets/PermissionMatrixEditor.js +6 -4
  101. package/dist/widgets/PositionSelect.d.ts +1 -1
  102. package/dist/widgets/PositionSelect.js +5 -4
  103. package/dist/widgets/PrintSheet.d.ts +15 -2
  104. package/dist/widgets/PrintSheet.js +3 -3
  105. package/dist/widgets/ProfileEditDiffCard.d.ts +1 -1
  106. package/dist/widgets/ProfileEditDiffCard.js +5 -4
  107. package/dist/widgets/QRDisplay.d.ts +1 -1
  108. package/dist/widgets/QRDisplay.js +5 -4
  109. package/dist/widgets/RankBoard.d.ts +1 -1
  110. package/dist/widgets/RankBoard.js +8 -6
  111. package/dist/widgets/RecordSheet.d.ts +1 -1
  112. package/dist/widgets/RecordSheet.js +2 -2
  113. package/dist/widgets/RoleMemberPicker.d.ts +1 -1
  114. package/dist/widgets/RoleMemberPicker.js +6 -5
  115. package/dist/widgets/SearchableMultiPicker.d.ts +1 -1
  116. package/dist/widgets/SearchableMultiPicker.js +7 -6
  117. package/dist/widgets/SecureScreen.d.ts +1 -1
  118. package/dist/widgets/SecureScreen.js +3 -3
  119. package/dist/widgets/StackedMeter.d.ts +1 -1
  120. package/dist/widgets/StackedMeter.js +2 -2
  121. package/dist/widgets/StagePeriodEditor.d.ts +1 -1
  122. package/dist/widgets/StagePeriodEditor.js +6 -5
  123. package/dist/widgets/Strip.d.ts +1 -1
  124. package/dist/widgets/Strip.js +2 -2
  125. package/dist/widgets/SummaryTable.d.ts +1 -1
  126. package/dist/widgets/SummaryTable.js +2 -2
  127. package/dist/widgets/TagInput.d.ts +1 -1
  128. package/dist/widgets/TagInput.js +3 -3
  129. package/dist/widgets/TagMultiSelect.d.ts +1 -1
  130. package/dist/widgets/TagMultiSelect.js +5 -4
  131. package/dist/widgets/Timeline.d.ts +1 -1
  132. package/dist/widgets/Timeline.js +4 -4
  133. package/dist/widgets/TreePicker.d.ts +1 -1
  134. package/dist/widgets/TreePicker.js +8 -7
  135. package/dist/widgets/TreeView.d.ts +17 -3
  136. package/dist/widgets/TreeView.js +7 -6
  137. package/dist/widgets/index.d.ts +3 -3
  138. package/dist/widgets/index.js +84 -83
  139. package/package.json +1 -1
  140. package/dist/chunk-LFEJLOFN.js +0 -352
  141. package/dist/chunk-MMIGOPO5.js +0 -111
  142. package/dist/chunk-TFGNKFL2.js +0 -39
  143. package/dist/chunk-XQPTSEJC.js +0 -135
@@ -1,4 +1,4 @@
1
- import { S as SduiBoxProps } from '../layout-C3nRXMwb.js';
1
+ import { S as SduiBoxProps } from '../layout-DmRikwZb.js';
2
2
  import React__default from 'react';
3
3
 
4
4
  interface ExcelTemplateColumn {
@@ -1,7 +1,8 @@
1
- import { loadOptionalModule } from '../chunk-YVTV4ZV6.js';
2
- import { useSduiAdapter, useSduiSandbox, hostFetchOf, defaultAdapter, hostRawFetchOf } from '../chunk-KO6OEY3G.js';
3
- import { Select, Input, Card, Button, Spinner, Icon, Alert, StatCard, Badge, Table, EmptyState } from '../chunk-RNOUXUBO.js';
4
- import { boxed } from '../chunk-5WW4MBRO.js';
1
+ import { loadOptionalModule } from '../chunk-4YPPL7JK.js';
2
+ import { useSduiAdapter, useSduiSandbox, hostFetchOf, defaultAdapter, hostRawFetchOf } from '../chunk-JHFA5RDN.js';
3
+ import '../chunk-GSM664TX.js';
4
+ import { Select, Input, Card, Button, Spinner, Icon, Alert, StatCard, Badge, Table, EmptyState } from '../chunk-YYAGE733.js';
5
+ import { boxed } from '../chunk-3JTBOABA.js';
5
6
  import { useRef, useState, useMemo, useCallback } from 'react';
6
7
  import { jsx, jsxs } from 'react/jsx-runtime';
7
8
 
package/dist/rich.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import React__default from 'react';
2
- export { b as DatePicker, D as DatePickerProps, c as DateRangePicker, a as DateRangePickerProps, d as DateRangeValue, e as TimePicker, T as TimePickerProps } from './DatePicker-C_TT_SOw.js';
3
- import './layout-C3nRXMwb.js';
2
+ export { b as DatePicker, D as DatePickerProps, c as DateRangePicker, a as DateRangePickerProps, d as DateRangeValue, e as TimePicker, T as TimePickerProps } from './DatePicker-DX9juElE.js';
3
+ import './layout-DmRikwZb.js';
4
4
 
5
5
  /**
6
6
  * Chart — 선언형 recharts 래퍼 (ERP `app/components/widgets/Chart.tsx` 이식).
@@ -26,6 +26,11 @@ interface ChartSeries {
26
26
  kind?: 'bar' | 'line' | 'dashed-line';
27
27
  /** 같은 문자열끼리 막대를 쌓는다 (stackId). */
28
28
  stack?: string;
29
+ /**
30
+ * 카르테시안 전용: 어느 y축에 그릴지. 기본 'left'. 단위가 다른 시리즈(원 막대 + km 선)를
31
+ * 한 축에 그리면 작은 쪽이 0에 붙는다 — 'right'를 주면 오른쪽 보조축이 생긴다.
32
+ */
33
+ axis?: 'left' | 'right';
29
34
  }
30
35
  interface ChartProps {
31
36
  type: ChartType;
@@ -59,8 +64,22 @@ interface ChartProps {
59
64
  };
60
65
  /** 데이터가 없을 때 보여줄 문구. */
61
66
  emptyText?: string;
67
+ /** 오른쪽 보조축(시리즈 axis:'right')의 값 포맷. 기본 'number'. 툴팁도 그 시리즈에는 이 포맷을 쓴다. */
68
+ rightValueFormat?: ChartValueFormat;
69
+ /**
70
+ * y축 눈금을 정수만 쓸지. 생략하면 축마다 자동 — 그 축에 그리는 값이 전부 정수(건수·인원)면
71
+ * 정수 눈금만 쓴다(0.3명·0.5건 같은 눈금이 나오지 않게). true/false는 두 축 모두에 강제한다.
72
+ */
73
+ integer?: boolean;
74
+ /**
75
+ * x축 범주 라벨을 **모두** 보인다. 긴 라벨은 칸 폭에 맞춰 줄을 바꾸고(최대 3줄),
76
+ * 그래도 모자라면 45° 기울인다(첫 라벨들이 왼쪽 밖으로 잘리지 않게 왼쪽 여백을 늘린다).
77
+ * 칸이 그보다도 좁으면 겹치는 라벨만 건너뛴다(기본 동작).
78
+ * 끄면(기본) recharts가 겹치는 라벨을 건너뛴다 — 본부명처럼 긴 범주에서 라벨이 사라진다.
79
+ */
80
+ xTickWrap?: boolean;
62
81
  }
63
- declare function Chart({ type, data, xKey, series, height, valueFormat, legend, legendPlacement, legendTotal, donutCenter, emptyText, }: ChartProps): React__default.JSX.Element;
82
+ declare function Chart({ type, data, xKey, series, height, valueFormat, legend, legendPlacement, legendTotal, donutCenter, emptyText, rightValueFormat, integer, xTickWrap, }: ChartProps): React__default.JSX.Element;
64
83
 
65
84
  /**
66
85
  * MapPreview — OpenLayers 기반 위치 미리보기 지도 (ERP `app/components/widgets/MapPreview.tsx` 이식).
package/dist/rich.js CHANGED
@@ -1,10 +1,11 @@
1
- import { useOptionalModule, DependencyPlaceholder } from './chunk-YVTV4ZV6.js';
2
- export { DatePicker, DateRangePicker, TimePicker } from './chunk-RWSH2MBU.js';
1
+ import { useOptionalModule, DependencyPlaceholder } from './chunk-4YPPL7JK.js';
2
+ export { DatePicker, DateRangePicker, TimePicker } from './chunk-FP32LNFK.js';
3
+ import './chunk-BITS7UJY.js';
3
4
  import { HoistedStyle } from './chunk-RIZRM7OO.js';
4
- import './chunk-XQPTSEJC.js';
5
- import { useSduiAssetResolver } from './chunk-KO6OEY3G.js';
6
- import { Spinner, EmptyState } from './chunk-RNOUXUBO.js';
7
- import './chunk-5WW4MBRO.js';
5
+ import { useSduiAssetResolver } from './chunk-JHFA5RDN.js';
6
+ import './chunk-GSM664TX.js';
7
+ import { Spinner, EmptyState } from './chunk-YYAGE733.js';
8
+ import './chunk-3JTBOABA.js';
8
9
  import { useRef, useCallback, useMemo, useState, useEffect, useLayoutEffect } from 'react';
9
10
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
10
11
 
@@ -47,24 +48,165 @@ function useSeriesPalette(ref) {
47
48
  return palette;
48
49
  }
49
50
  var formatNumber = (v) => v.toLocaleString("ko-KR", { maximumFractionDigits: 1 });
51
+ var eokText = (abs) => {
52
+ const eok = abs / 1e8;
53
+ return `${eok.toLocaleString("ko-KR", { maximumFractionDigits: eok >= 100 ? 0 : 1 })}\uC5B5`;
54
+ };
55
+ var manText = (abs) => {
56
+ const man = abs / 1e4;
57
+ return `${man.toLocaleString("ko-KR", { maximumFractionDigits: man >= 100 ? 0 : 1 })}\uB9CC`;
58
+ };
50
59
  var formatCurrency = (v) => {
51
60
  const sign = v < 0 ? "-" : "";
52
61
  const abs = Math.abs(v);
53
- if (abs >= 1e8) {
54
- const eok = abs / 1e8;
55
- return `${sign}${eok.toLocaleString("ko-KR", { maximumFractionDigits: eok >= 100 ? 0 : 1 })}\uC5B5`;
56
- }
57
- if (abs >= 1e4) {
58
- return `${sign}${Math.round(abs / 1e4).toLocaleString("ko-KR")}\uB9CC`;
59
- }
62
+ if (abs >= 1e8) return `${sign}${eokText(abs)}`;
63
+ if (abs >= 1e4) return `${sign}${manText(abs)}`;
60
64
  return `${sign}${abs.toLocaleString("ko-KR")}`;
61
65
  };
66
+ var currencyAxisFormatter = (maxAbs) => {
67
+ if (!(maxAbs >= 1e5)) return (v) => v.toLocaleString("ko-KR", { maximumFractionDigits: 1 });
68
+ return (v) => {
69
+ const abs = Math.abs(v);
70
+ if (abs === 0) return "0";
71
+ const sign = v < 0 ? "-" : "";
72
+ return `${sign}${abs >= 1e8 ? eokText(abs) : manText(abs)}`;
73
+ };
74
+ };
62
75
  var formatPercent = (v) => `${v.toLocaleString("ko-KR", { maximumFractionDigits: 1 })}%`;
63
76
  var getFormatter = (format) => {
64
77
  if (format === "currency") return formatCurrency;
65
78
  if (format === "percent") return formatPercent;
66
79
  return formatNumber;
67
80
  };
81
+ var axisFormatter = (format, extent) => format === "currency" ? currencyAxisFormatter(extent.maxAbs) : getFormatter(format);
82
+ function axisExtent(rows, series) {
83
+ let maxAbs = 0;
84
+ let allInteger = true;
85
+ for (const row of rows) {
86
+ if (row === null || typeof row !== "object") continue;
87
+ const positive = /* @__PURE__ */ new Map();
88
+ const negative = /* @__PURE__ */ new Map();
89
+ for (const s of series) {
90
+ const raw = row[s.key];
91
+ if (raw === null || raw === void 0 || raw === "" || typeof raw === "boolean") continue;
92
+ const n = Number(raw);
93
+ if (!Number.isFinite(n)) continue;
94
+ if (!Number.isInteger(n)) allInteger = false;
95
+ if (s.stack) {
96
+ const bucket = n >= 0 ? positive : negative;
97
+ bucket.set(s.stack, (bucket.get(s.stack) ?? 0) + n);
98
+ } else {
99
+ maxAbs = Math.max(maxAbs, Math.abs(n));
100
+ }
101
+ }
102
+ for (const total of [...positive.values(), ...negative.values()]) {
103
+ maxAbs = Math.max(maxAbs, Math.abs(total));
104
+ }
105
+ }
106
+ return { maxAbs, allInteger };
107
+ }
108
+ function estimateTextWidth(text, fontSize = 12) {
109
+ let w = 0;
110
+ for (const ch of text) {
111
+ if (/\s/.test(ch)) w += fontSize * 0.3;
112
+ else if (/[\u1100-\u11ff\u3000-\u9fff\uac00-\ud7af\uf900-\ufaff\uff00-\uffef]/.test(ch)) w += fontSize;
113
+ else w += fontSize * 0.6;
114
+ }
115
+ return w;
116
+ }
117
+ function wrapLabel(label, maxWidth, measure) {
118
+ const text = label.trim();
119
+ if (!text) return [""];
120
+ if (!(maxWidth > 0) || measure(text) <= maxWidth) return [text];
121
+ const lines = [];
122
+ let line = "";
123
+ const push = () => {
124
+ if (line.trim()) lines.push(line.trim());
125
+ line = "";
126
+ };
127
+ for (const word of text.split(/(\s+)/)) {
128
+ if (!word) continue;
129
+ const candidate = line + word;
130
+ if (measure(candidate.trim()) <= maxWidth) {
131
+ line = candidate;
132
+ continue;
133
+ }
134
+ if (/^\s+$/.test(word)) {
135
+ push();
136
+ continue;
137
+ }
138
+ push();
139
+ if (measure(word) <= maxWidth) {
140
+ line = word;
141
+ continue;
142
+ }
143
+ const chars = [...word];
144
+ const total = measure(word);
145
+ const parts = Math.ceil(total / maxWidth);
146
+ let acc = 0;
147
+ let boundary = total / parts;
148
+ for (const ch of chars) {
149
+ const w = measure(ch);
150
+ if (line && acc + w / 2 > boundary + 0.01) {
151
+ push();
152
+ boundary += total / parts;
153
+ }
154
+ line += ch;
155
+ acc += w;
156
+ }
157
+ }
158
+ push();
159
+ return lines.length > 0 ? lines : [text];
160
+ }
161
+ var TICK_FONT = 12;
162
+ var TICK_LINE_H = 14;
163
+ var MAX_TICK_LINES = 3;
164
+ function planXTicks(labels, bandWidth, measure) {
165
+ if (!(bandWidth > 0) || labels.length === 0) return { mode: "skip" };
166
+ const room = Math.max(0, bandWidth - 6);
167
+ const lines = /* @__PURE__ */ new Map();
168
+ let maxLines = 1;
169
+ let fits = room >= TICK_FONT;
170
+ for (const label of labels) {
171
+ const wrapped = wrapLabel(label, room, measure);
172
+ lines.set(label.trim(), wrapped);
173
+ maxLines = Math.max(maxLines, wrapped.length);
174
+ if (wrapped.some((line) => measure(line) > room + TICK_FONT / 2)) fits = false;
175
+ }
176
+ if (fits && maxLines <= MAX_TICK_LINES) {
177
+ return { mode: "wrap", lines, height: Math.ceil(8 + maxLines * TICK_LINE_H + 4) };
178
+ }
179
+ if (bandWidth * Math.SQRT1_2 >= TICK_FONT * 1.25) {
180
+ const longest = Math.max(...labels.map((label) => measure(label)));
181
+ return { mode: "rotate", height: Math.ceil(8 + (longest + TICK_FONT) * Math.SQRT1_2 + 4) };
182
+ }
183
+ return { mode: "skip" };
184
+ }
185
+ var ROTATE_EDGE_PAD = 2;
186
+ function rotatedTickInset(widths, axisWidth, span, point = false) {
187
+ const n = widths.length;
188
+ if (n === 0 || !(span > 0)) return 0;
189
+ let inset = 0;
190
+ widths.forEach((width, i) => {
191
+ const f = point ? n > 1 ? i / (n - 1) : 0.5 : (i + 0.5) / n;
192
+ if (f >= 1) return;
193
+ const reach = (width + TICK_FONT / 2) * Math.SQRT1_2 + ROTATE_EDGE_PAD;
194
+ const need = (reach - axisWidth - span * f) / (1 - f);
195
+ if (need > inset) inset = need;
196
+ });
197
+ return Math.ceil(inset);
198
+ }
199
+ var ROTATE_MIN_BAND = TICK_FONT * 1.25 / Math.SQRT1_2;
200
+ function planXTicksWithInset(labels, span, axisWidth, measure, point = false) {
201
+ const count = Math.max(1, labels.length);
202
+ const plan = planXTicks(labels, span / count, measure);
203
+ if (plan.mode !== "rotate") return { plan, inset: 0 };
204
+ const want = rotatedTickInset(labels.map((label) => measure(label)), axisWidth, span, point);
205
+ const inset = Math.max(0, Math.min(want, Math.floor(span - count * ROTATE_MIN_BAND)));
206
+ if (inset === 0) return { plan, inset: 0 };
207
+ const narrowed = planXTicks(labels, (span - inset) / count, measure);
208
+ return narrowed.mode === "rotate" ? { plan: narrowed, inset } : { plan, inset: 0 };
209
+ }
68
210
  var tooltipBoxStyle = {
69
211
  background: "var(--wp-surface-card)",
70
212
  border: "1px solid var(--wp-border-subtle)",
@@ -80,7 +222,7 @@ var dotStyle = (color) => ({
80
222
  background: color,
81
223
  flexShrink: 0
82
224
  });
83
- function renderTooltipContent(props, format) {
225
+ function renderTooltipContent(props, formatOf) {
84
226
  const { active, payload, label } = props;
85
227
  if (!active || !payload || payload.length === 0) return null;
86
228
  return /* @__PURE__ */ jsxs("div", { style: tooltipBoxStyle, children: [
@@ -88,7 +230,7 @@ function renderTooltipContent(props, format) {
88
230
  /* @__PURE__ */ jsx("div", { style: { display: "grid", gap: 2 }, children: payload.filter((entry) => entry.type !== "none").map((entry, i) => {
89
231
  const fillFromDatum = entry.payload !== null && typeof entry.payload === "object" ? entry.payload.fill : void 0;
90
232
  const color = entry.color ?? fillFromDatum ?? "var(--wp-text-muted)";
91
- const value = typeof entry.value === "number" ? format(entry.value) : String(entry.value ?? "");
233
+ const value = typeof entry.value === "number" ? formatOf(entry)(entry.value) : String(entry.value ?? "");
92
234
  return /* @__PURE__ */ jsxs(
93
235
  "div",
94
236
  {
@@ -169,6 +311,9 @@ function SliceLegendTable({
169
311
  style: {
170
312
  flex: "none",
171
313
  minWidth: 148,
314
+ // 차트 옆 한 줄에서는 차트가 남는 폭을 다 먹어 여백이 0이다. 좁아서 아래 줄로
315
+ // 내려가면(wp-chart__split 이 wrap) 혼자 남은 줄에서 가운데에 선다.
316
+ marginInline: "auto",
172
317
  display: "flex",
173
318
  flexDirection: "column",
174
319
  justifyContent: "center",
@@ -208,7 +353,60 @@ function SliceLegendTable({
208
353
  }
209
354
  );
210
355
  }
356
+ var measureCanvas;
357
+ function makeMeasure(fontFamily) {
358
+ if (measureCanvas === void 0) {
359
+ try {
360
+ measureCanvas = typeof document !== "undefined" ? document.createElement("canvas").getContext("2d") : null;
361
+ } catch {
362
+ measureCanvas = null;
363
+ }
364
+ }
365
+ const ctx = measureCanvas;
366
+ if (!ctx || !fontFamily) return (text) => estimateTextWidth(text, TICK_FONT);
367
+ const font = `${TICK_FONT}px ${fontFamily}`;
368
+ return (text) => {
369
+ ctx.font = font;
370
+ return ctx.measureText(text).width;
371
+ };
372
+ }
373
+ function useHostBox(ref, enabled) {
374
+ const [box, setBox] = useState({
375
+ width: 0,
376
+ fontFamily: null
377
+ });
378
+ useLayoutEffect(() => {
379
+ const el = ref.current;
380
+ if (!enabled || el === null) return;
381
+ const read = () => {
382
+ const width = Math.round(el.getBoundingClientRect().width);
383
+ const fontFamily = getComputedStyle(el).fontFamily || null;
384
+ setBox((prev) => prev.width === width && prev.fontFamily === fontFamily ? prev : { width, fontFamily });
385
+ };
386
+ read();
387
+ if (typeof ResizeObserver === "undefined") return;
388
+ const observer = new ResizeObserver(read);
389
+ observer.observe(el);
390
+ return () => observer.disconnect();
391
+ });
392
+ return box;
393
+ }
211
394
  var axisTick = { fill: "var(--wp-text-muted)", fontSize: 12 };
395
+ var SIDE_LEGEND_CHART_MIN = 96;
396
+ function renderCategoryTick(plan) {
397
+ const CategoryTick = (props) => {
398
+ const x = Number(props.x) || 0;
399
+ const y = Number(props.y) || 0;
400
+ const label = String(props.payload?.value ?? "");
401
+ const textStyle = { fill: axisTick.fill, fontSize: axisTick.fontSize };
402
+ if (plan.mode === "rotate") {
403
+ return /* @__PURE__ */ jsx("g", { transform: `translate(${x},${y})`, children: /* @__PURE__ */ jsx("text", { x: 0, y: 0, dy: "0.71em", textAnchor: "end", transform: "rotate(-45)", style: textStyle, children: label }) });
404
+ }
405
+ const lines = plan.lines.get(label.trim()) ?? [label];
406
+ return /* @__PURE__ */ jsx("g", { transform: `translate(${x},${y})`, children: /* @__PURE__ */ jsx("text", { x: 0, y: 0, textAnchor: "middle", style: textStyle, children: lines.map((line, i) => /* @__PURE__ */ jsx("tspan", { x: 0, dy: i === 0 ? "0.71em" : TICK_LINE_H, children: line }, i)) }) });
407
+ };
408
+ return CategoryTick;
409
+ }
212
410
  function Chart({
213
411
  type,
214
412
  data,
@@ -220,11 +418,16 @@ function Chart({
220
418
  legendPlacement = "bottom",
221
419
  legendTotal,
222
420
  donutCenter,
223
- emptyText
421
+ emptyText,
422
+ rightValueFormat = "number",
423
+ integer,
424
+ xTickWrap = false
224
425
  }) {
225
426
  const hostRef = useRef(null);
226
427
  const palette = useSeriesPalette(hostRef);
227
428
  const recharts = useOptionalModule(loadRecharts);
429
+ const cartesianType = type === "bar" || type === "line" || type === "composed";
430
+ const hostBox = useHostBox(hostRef, xTickWrap === true && cartesianType);
228
431
  const resolveColor = useCallback(
229
432
  (color, index) => {
230
433
  if (!color) return palette[index % palette.length];
@@ -270,7 +473,7 @@ function Chart({
270
473
  XAxis,
271
474
  YAxis
272
475
  } = recharts.module;
273
- const tooltipContent = (props) => renderTooltipContent(props, format);
476
+ const tooltipContent = (props) => renderTooltipContent(props, () => format);
274
477
  if (type === "donut" || type === "pie") {
275
478
  const nameKey = xKey ?? "name";
276
479
  const valueKey = seriesList[0].key;
@@ -283,7 +486,7 @@ function Chart({
283
486
  const chartArea = /* @__PURE__ */ jsxs(
284
487
  "div",
285
488
  {
286
- style: sideLegend ? { position: "relative", flex: "1 1 0%", minWidth: 0, height } : { position: "relative", width: "100%", height },
489
+ style: sideLegend ? { position: "relative", flex: `1 1 ${SIDE_LEGEND_CHART_MIN}px`, minWidth: 0, height } : { position: "relative", width: "100%", height },
287
490
  children: [
288
491
  /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxs(PieChart, { children: [
289
492
  /* @__PURE__ */ jsx(Tooltip, { content: tooltipContent }),
@@ -333,17 +536,24 @@ function Chart({
333
536
  };
334
537
  const denomOverride = Number(legendTotal);
335
538
  return shell(
336
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "var(--wp-space-6)" }, children: [
337
- chartArea,
338
- /* @__PURE__ */ jsx(
339
- SliceLegendTable,
340
- {
341
- rows: rows.map((row, i) => ({ ...legendItems2[i], value: sliceValue(row) })),
342
- format,
343
- total: legendTotal !== void 0 && Number.isFinite(denomOverride) ? denomOverride : void 0
344
- }
345
- )
346
- ] })
539
+ /* @__PURE__ */ jsxs(
540
+ "div",
541
+ {
542
+ className: "wp-chart__split",
543
+ style: { display: "flex", flexWrap: "wrap", alignItems: "center", gap: "var(--wp-space-6)" },
544
+ children: [
545
+ chartArea,
546
+ /* @__PURE__ */ jsx(
547
+ SliceLegendTable,
548
+ {
549
+ rows: rows.map((row, i) => ({ ...legendItems2[i], value: sliceValue(row) })),
550
+ format,
551
+ total: legendTotal !== void 0 && Number.isFinite(denomOverride) ? denomOverride : void 0
552
+ }
553
+ )
554
+ ]
555
+ }
556
+ )
347
557
  );
348
558
  }
349
559
  return shell(
@@ -369,6 +579,25 @@ function Chart({
369
579
  color: resolveColor(s.color, i),
370
580
  dashed: kindOf(s) === "dashed-line"
371
581
  }));
582
+ const onRight = (s) => s.axis === "right";
583
+ const dual = seriesList.some(onRight) && seriesList.some((s) => !onRight(s));
584
+ const axisIdOf = (s) => dual ? onRight(s) ? "right" : "left" : void 0;
585
+ const extentOf = (list) => axisExtent(
586
+ rows,
587
+ list.map((s) => ({ key: s.key, stack: kindOf(s) === "bar" ? s.stack : void 0 }))
588
+ );
589
+ const leftExtent = extentOf(dual ? seriesList.filter((s) => !onRight(s)) : seriesList);
590
+ const rightExtent = dual ? extentOf(seriesList.filter(onRight)) : null;
591
+ const allowDecimals = (extent) => typeof integer === "boolean" ? !integer : !extent.allInteger;
592
+ const tickCountFor = (extent) => !allowDecimals(extent) && extent.maxAbs < 4 ? Math.max(2, Math.ceil(extent.maxAbs) + 1) : void 0;
593
+ const leftTickFormat = axisFormatter(valueFormat, leftExtent);
594
+ const rightFormat = getFormatter(rightValueFormat);
595
+ const rightTickFormat = rightExtent ? axisFormatter(rightValueFormat, rightExtent) : rightFormat;
596
+ const rightKeys = new Set(dual ? seriesList.filter(onRight).map((s) => s.key) : []);
597
+ const cartesianTooltip = (props) => renderTooltipContent(
598
+ props,
599
+ (entry) => typeof entry.dataKey === "string" && rightKeys.has(entry.dataKey) ? rightFormat : format
600
+ );
372
601
  const marks = seriesList.map((s, i) => {
373
602
  const color = resolveColor(s.color, i);
374
603
  const name = s.label ?? s.key;
@@ -380,6 +609,7 @@ function Chart({
380
609
  name,
381
610
  fill: color,
382
611
  stackId: s.stack,
612
+ yAxisId: axisIdOf(s),
383
613
  maxBarSize: 48,
384
614
  radius: barRadius(s, i),
385
615
  isAnimationActive: false
@@ -396,6 +626,7 @@ function Chart({
396
626
  stroke: color,
397
627
  strokeWidth: 2,
398
628
  strokeDasharray: kindOf(s) === "dashed-line" ? "6 4" : void 0,
629
+ yAxisId: axisIdOf(s),
399
630
  dot: false,
400
631
  activeDot: { r: 4, strokeWidth: 2, stroke: "var(--wp-surface-card)" },
401
632
  isAnimationActive: false
@@ -403,8 +634,29 @@ function Chart({
403
634
  s.key
404
635
  );
405
636
  });
637
+ const margin = { top: 8, right: 8, bottom: 0, left: 0 };
638
+ const Y_AXIS_WIDTH = 52;
639
+ let xTick = {};
640
+ if (xTickWrap && hostBox.width > 0) {
641
+ const labels = rows.map((row) => String((xKey ? row[xKey] : void 0) ?? ""));
642
+ const measure = makeMeasure(hostBox.fontFamily);
643
+ const span = hostBox.width - margin.right - Y_AXIS_WIDTH * (dual ? 2 : 1);
644
+ const { plan, inset } = planXTicksWithInset(labels, span - margin.left, Y_AXIS_WIDTH, measure, !hasBar);
645
+ margin.left += inset;
646
+ if (plan.mode !== "skip") {
647
+ xTick = { interval: 0, height: plan.height, tick: renderCategoryTick(plan) };
648
+ }
649
+ }
406
650
  const axes = /* @__PURE__ */ jsxs(Fragment, { children: [
407
- /* @__PURE__ */ jsx(CartesianGrid, { strokeDasharray: "3 3", stroke: "var(--wp-divider)", vertical: false }),
651
+ /* @__PURE__ */ jsx(
652
+ CartesianGrid,
653
+ {
654
+ strokeDasharray: "3 3",
655
+ stroke: "var(--wp-divider)",
656
+ vertical: false,
657
+ yAxisId: dual ? "left" : void 0
658
+ }
659
+ ),
408
660
  /* @__PURE__ */ jsx(
409
661
  XAxis,
410
662
  {
@@ -412,28 +664,45 @@ function Chart({
412
664
  tickLine: false,
413
665
  axisLine: { stroke: "var(--wp-border-subtle)" },
414
666
  tick: axisTick,
415
- tickMargin: 6
667
+ tickMargin: 6,
668
+ ...xTick
416
669
  }
417
670
  ),
418
671
  /* @__PURE__ */ jsx(
419
672
  YAxis,
420
673
  {
674
+ yAxisId: dual ? "left" : void 0,
421
675
  tickLine: false,
422
676
  axisLine: false,
423
677
  tick: axisTick,
424
- tickFormatter: (v) => format(v),
425
- width: 52
678
+ tickFormatter: (v) => leftTickFormat(v),
679
+ allowDecimals: allowDecimals(leftExtent),
680
+ tickCount: tickCountFor(leftExtent),
681
+ width: Y_AXIS_WIDTH
426
682
  }
427
683
  ),
684
+ dual && rightExtent ? /* @__PURE__ */ jsx(
685
+ YAxis,
686
+ {
687
+ yAxisId: "right",
688
+ orientation: "right",
689
+ tickLine: false,
690
+ axisLine: false,
691
+ tick: axisTick,
692
+ tickFormatter: (v) => rightTickFormat(v),
693
+ allowDecimals: allowDecimals(rightExtent),
694
+ tickCount: tickCountFor(rightExtent),
695
+ width: Y_AXIS_WIDTH
696
+ }
697
+ ) : null,
428
698
  /* @__PURE__ */ jsx(
429
699
  Tooltip,
430
700
  {
431
701
  cursor: hasBar ? { fill: "var(--wp-surface-sunken)", fillOpacity: 0.5 } : { stroke: "var(--wp-border-subtle)", strokeDasharray: "3 3" },
432
- content: tooltipContent
702
+ content: cartesianTooltip
433
703
  }
434
704
  )
435
705
  ] });
436
- const margin = { top: 8, right: 8, bottom: 0, left: 0 };
437
706
  const cartesian = type === "bar" ? /* @__PURE__ */ jsxs(BarChart, { data: rows, margin, children: [
438
707
  axes,
439
708
  marks