@esic-lab/data-core-ui 0.0.57 → 0.0.59
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.css +58 -12
- package/dist/index.d.mts +18 -2
- package/dist/index.d.ts +18 -2
- package/dist/index.js +474 -19
- package/dist/index.mjs +482 -19
- package/package.json +9 -3
package/dist/index.js
CHANGED
|
@@ -369,6 +369,7 @@ __export(index_exports, {
|
|
|
369
369
|
DatePickerRange: () => DatePickerRange,
|
|
370
370
|
FileUploader: () => FileUploader,
|
|
371
371
|
FilterPopUp: () => FilterPopUp,
|
|
372
|
+
GanttChart: () => GanttChart,
|
|
372
373
|
GhostButton: () => GhostButton,
|
|
373
374
|
HeadingPage: () => HeadingPage,
|
|
374
375
|
Indicator: () => Indicator,
|
|
@@ -4110,7 +4111,8 @@ function ProfileSelect({
|
|
|
4110
4111
|
assignUser,
|
|
4111
4112
|
mode,
|
|
4112
4113
|
className,
|
|
4113
|
-
onUpdateAssignUser
|
|
4114
|
+
onUpdateAssignUser,
|
|
4115
|
+
placeholder
|
|
4114
4116
|
}) {
|
|
4115
4117
|
const [maxVisible, setMaxVisible] = (0, import_react20.useState)(4);
|
|
4116
4118
|
const [userNotAssign, setUserNotAssign] = (0, import_react20.useState)([]);
|
|
@@ -4156,9 +4158,7 @@ function ProfileSelect({
|
|
|
4156
4158
|
const visibleUsers = showPlus ? assignUser.slice(0, maxVisible - 1) : assignUser;
|
|
4157
4159
|
const extraCount = assignUser.length - (maxVisible - 1);
|
|
4158
4160
|
const normalizedSearch = search.trim().toLowerCase();
|
|
4159
|
-
const filteredAssigned = normalizedSearch ? assignUser.filter(
|
|
4160
|
-
(u) => u.name.toLowerCase().includes(normalizedSearch)
|
|
4161
|
-
) : assignUser;
|
|
4161
|
+
const filteredAssigned = normalizedSearch ? assignUser.filter((u) => u.name.toLowerCase().includes(normalizedSearch)) : assignUser;
|
|
4162
4162
|
const filteredUnassigned = normalizedSearch ? userNotAssign.filter(
|
|
4163
4163
|
(u) => u.name.toLowerCase().includes(normalizedSearch)
|
|
4164
4164
|
) : userNotAssign;
|
|
@@ -4170,13 +4170,13 @@ function ProfileSelect({
|
|
|
4170
4170
|
className: "p-2 border rounded cursor-pointer bg-white",
|
|
4171
4171
|
onClick: () => setIsShowSelect(!isShowSelect)
|
|
4172
4172
|
}
|
|
4173
|
-
) }) : mode === "showAssign" ? /* @__PURE__ */ (0, import_jsx_runtime42.
|
|
4173
|
+
) }) : mode === "showAssign" ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
4174
4174
|
"div",
|
|
4175
4175
|
{
|
|
4176
|
-
className: `w-full h-[40px] flex -
|
|
4176
|
+
className: `w-full h-[40px] flex items-center p-2 cursor-pointer ${className}`,
|
|
4177
4177
|
onClick: () => setIsShowSelect(!isShowSelect),
|
|
4178
|
-
children: [
|
|
4179
|
-
visibleUsers.map((user) => /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { className: "flex items-center
|
|
4178
|
+
children: visibleUsers.length === 0 ? placeholder ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "body-1 text-gray-400 select-none", children: placeholder }) : null : /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(import_jsx_runtime42.Fragment, { children: [
|
|
4179
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { className: "flex -space-x-2", children: visibleUsers.map((user) => /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { className: "flex items-center", children: /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("label", { className: "relative group cursor-pointer", children: [
|
|
4180
4180
|
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
4181
4181
|
"img",
|
|
4182
4182
|
{
|
|
@@ -4193,16 +4193,19 @@ function ProfileSelect({
|
|
|
4193
4193
|
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
4194
4194
|
"span",
|
|
4195
4195
|
{
|
|
4196
|
-
className: "absolute top-0 right-0 -translate-y-2 translate-x-2 \r\n
|
|
4197
|
-
onClick: () =>
|
|
4196
|
+
className: "absolute top-0 right-0 -translate-y-2 translate-x-2 \r\n rounded-full bg-white opacity-0 group-hover:opacity-100 \r\n border-3 z-10 border-red-500 flex items-center justify-center transition",
|
|
4197
|
+
onClick: (e) => {
|
|
4198
|
+
e.stopPropagation();
|
|
4199
|
+
onUpdateAssignUser(user, "remove");
|
|
4200
|
+
},
|
|
4198
4201
|
children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_icons_react14.IconX, { className: "w-4 h-4 text-red-500" })
|
|
4199
4202
|
}
|
|
4200
4203
|
)
|
|
4201
|
-
] }) }, user.id)),
|
|
4204
|
+
] }) }, user.id)) }),
|
|
4202
4205
|
showPlus && /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
|
|
4203
4206
|
"div",
|
|
4204
4207
|
{
|
|
4205
|
-
className: "border border-white flex items-center justify-center bg-gray-300 text-black text-sm",
|
|
4208
|
+
className: "border border-white flex items-center justify-center bg-gray-300 text-black text-sm ml-2",
|
|
4206
4209
|
style: {
|
|
4207
4210
|
width: avatarSize,
|
|
4208
4211
|
height: avatarSize,
|
|
@@ -4214,7 +4217,7 @@ function ProfileSelect({
|
|
|
4214
4217
|
]
|
|
4215
4218
|
}
|
|
4216
4219
|
)
|
|
4217
|
-
]
|
|
4220
|
+
] })
|
|
4218
4221
|
}
|
|
4219
4222
|
) : /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: `w-full h-[40px] flex -space-x-2 p-2 ${className}`, children: [
|
|
4220
4223
|
visibleUsers.map((user) => /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { className: "flex items-center ", children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("label", { className: "relative group ", children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
@@ -4345,7 +4348,7 @@ var QRCodeGenerator = ({
|
|
|
4345
4348
|
fileBaseName = "qr-code"
|
|
4346
4349
|
}) => {
|
|
4347
4350
|
const canvasRef = (0, import_react21.useRef)(null);
|
|
4348
|
-
const [
|
|
4351
|
+
const [format5, setFormat] = (0, import_react21.useState)("png");
|
|
4349
4352
|
const [exportSize, setExportSize] = (0, import_react21.useState)(defaultExportSize);
|
|
4350
4353
|
const sizeOption = [
|
|
4351
4354
|
{
|
|
@@ -4399,7 +4402,7 @@ var QRCodeGenerator = ({
|
|
|
4399
4402
|
}, [url, previewSize]);
|
|
4400
4403
|
const download = async () => {
|
|
4401
4404
|
try {
|
|
4402
|
-
if (
|
|
4405
|
+
if (format5 === "svg") {
|
|
4403
4406
|
const svgString = await import_qrcode.default.toString(url, {
|
|
4404
4407
|
type: "svg",
|
|
4405
4408
|
width: exportSize,
|
|
@@ -4415,9 +4418,9 @@ var QRCodeGenerator = ({
|
|
|
4415
4418
|
width: exportSize,
|
|
4416
4419
|
margin: 1
|
|
4417
4420
|
});
|
|
4418
|
-
const mime =
|
|
4419
|
-
const dataURL =
|
|
4420
|
-
triggerDownload(dataURL, `${fileBaseName}.${
|
|
4421
|
+
const mime = format5 === "png" ? "image/png" : "image/jpeg";
|
|
4422
|
+
const dataURL = format5 === "jpeg" ? offscreen.toDataURL(mime, 0.92) : offscreen.toDataURL(mime);
|
|
4423
|
+
triggerDownload(dataURL, `${fileBaseName}.${format5}`);
|
|
4421
4424
|
}
|
|
4422
4425
|
} catch (err) {
|
|
4423
4426
|
console.error("Failed to generate QR export:", err);
|
|
@@ -4450,7 +4453,7 @@ var QRCodeGenerator = ({
|
|
|
4450
4453
|
SelectField,
|
|
4451
4454
|
{
|
|
4452
4455
|
label: "\u0E23\u0E39\u0E1B\u0E41\u0E1A\u0E1A",
|
|
4453
|
-
value:
|
|
4456
|
+
value: format5,
|
|
4454
4457
|
onChange: (e) => setFormat(e.target.value),
|
|
4455
4458
|
options: typeOption
|
|
4456
4459
|
}
|
|
@@ -4684,6 +4687,457 @@ var PieChart = ({
|
|
|
4684
4687
|
] })
|
|
4685
4688
|
] });
|
|
4686
4689
|
};
|
|
4690
|
+
|
|
4691
|
+
// src/GanttChart/GanttChart.tsx
|
|
4692
|
+
var import_react24 = require("react");
|
|
4693
|
+
var d33 = __toESM(require("d3"));
|
|
4694
|
+
var import_date_fns3 = require("date-fns");
|
|
4695
|
+
var import_jsx_runtime47 = require("react/jsx-runtime");
|
|
4696
|
+
var LAYOUT = {
|
|
4697
|
+
barHeight: 40,
|
|
4698
|
+
barSpacing: 10,
|
|
4699
|
+
headersGroupLayer1Height: 30,
|
|
4700
|
+
headersGroupLayer2Height: 35
|
|
4701
|
+
};
|
|
4702
|
+
var HEADER_FONTS = {
|
|
4703
|
+
layer1: 16,
|
|
4704
|
+
layer2: 12
|
|
4705
|
+
};
|
|
4706
|
+
var VIEW_MODE_SCALE = {
|
|
4707
|
+
year: 0.5,
|
|
4708
|
+
month: 2,
|
|
4709
|
+
week: 10
|
|
4710
|
+
};
|
|
4711
|
+
var STATUS_META = {
|
|
4712
|
+
pending: { color: "#F16965", label: "\u0E23\u0E2D\u0E14\u0E33\u0E40\u0E19\u0E34\u0E19\u0E01\u0E32\u0E23" },
|
|
4713
|
+
"in-progress": { color: "#FFBB33", label: "\u0E01\u0E33\u0E25\u0E31\u0E07\u0E14\u0E33\u0E40\u0E19\u0E34\u0E19\u0E01\u0E32\u0E23" },
|
|
4714
|
+
completed: { color: "#69C57D", label: "\u0E40\u0E2A\u0E23\u0E47\u0E08\u0E2A\u0E34\u0E49\u0E19" }
|
|
4715
|
+
};
|
|
4716
|
+
var THAI_MONTHS = {
|
|
4717
|
+
January: "\u0E21.\u0E04.",
|
|
4718
|
+
February: "\u0E01.\u0E1E.",
|
|
4719
|
+
March: "\u0E21\u0E35.\u0E04.",
|
|
4720
|
+
April: "\u0E40\u0E21.\u0E22.",
|
|
4721
|
+
May: "\u0E1E.\u0E04.",
|
|
4722
|
+
June: "\u0E21\u0E34.\u0E22.",
|
|
4723
|
+
July: "\u0E01.\u0E04.",
|
|
4724
|
+
August: "\u0E2A.\u0E04.",
|
|
4725
|
+
September: "\u0E01.\u0E22.",
|
|
4726
|
+
October: "\u0E15.\u0E04.",
|
|
4727
|
+
November: "\u0E1E.\u0E22.",
|
|
4728
|
+
December: "\u0E18.\u0E04."
|
|
4729
|
+
};
|
|
4730
|
+
var THAI_DAYS = ["\u0E2D\u0E32.", "\u0E08.", "\u0E2D.", "\u0E1E.", "\u0E1E\u0E24.", "\u0E28.", "\u0E2A."];
|
|
4731
|
+
var roundedRectPath = (x, y, width, height, radius) => `M${x + radius},${y} H${x + width} V${y + height} H${x + radius} A${radius},${radius},0,0,1,${x},${y + height - radius} V${y + radius} A${radius},${radius},0,0,1,${x + radius},${y}`;
|
|
4732
|
+
var formatThaiMonth = (date) => {
|
|
4733
|
+
const monthName = (0, import_date_fns3.format)(date, "MMMM");
|
|
4734
|
+
return THAI_MONTHS[monthName] || monthName;
|
|
4735
|
+
};
|
|
4736
|
+
var formatThaiDate = (date) => date ? date.toLocaleDateString("th-TH", { day: "numeric", month: "short" }) : "-";
|
|
4737
|
+
var isDateOnlyValue = (dt) => {
|
|
4738
|
+
const localZero = dt.getHours() === 0 && dt.getMinutes() === 0 && dt.getSeconds() === 0 && dt.getMilliseconds() === 0;
|
|
4739
|
+
const utcZero = dt.getUTCHours() === 0 && dt.getUTCMinutes() === 0 && dt.getUTCSeconds() === 0 && dt.getUTCMilliseconds() === 0;
|
|
4740
|
+
return localZero || utcZero;
|
|
4741
|
+
};
|
|
4742
|
+
var getAdjustedStart = (d) => {
|
|
4743
|
+
if (!d.startDate && d.endDate) {
|
|
4744
|
+
const endDate = d.endDate;
|
|
4745
|
+
const quarter = Math.floor(endDate.getMonth() / 3);
|
|
4746
|
+
const monthIndex = quarter * 3;
|
|
4747
|
+
return (0, import_date_fns3.startOfDay)(new Date(endDate.getFullYear(), monthIndex, 1));
|
|
4748
|
+
}
|
|
4749
|
+
if (d.startDate) {
|
|
4750
|
+
return isDateOnlyValue(d.startDate) ? (0, import_date_fns3.startOfDay)(d.startDate) : d.startDate;
|
|
4751
|
+
}
|
|
4752
|
+
return (0, import_date_fns3.startOfDay)(/* @__PURE__ */ new Date());
|
|
4753
|
+
};
|
|
4754
|
+
var getAdjustedEnd = (d) => {
|
|
4755
|
+
if (!d.startDate && d.endDate) {
|
|
4756
|
+
const endDate = d.endDate;
|
|
4757
|
+
const quarter = Math.floor(endDate.getMonth() / 3);
|
|
4758
|
+
const endMonth = (quarter + 1) * 3;
|
|
4759
|
+
return (0, import_date_fns3.startOfDay)((0, import_date_fns3.addDays)(new Date(endDate.getFullYear(), endMonth, 0), 1));
|
|
4760
|
+
}
|
|
4761
|
+
if (d.endDate) {
|
|
4762
|
+
return isDateOnlyValue(d.endDate) ? (0, import_date_fns3.startOfDay)((0, import_date_fns3.addDays)(d.endDate, 1)) : d.endDate;
|
|
4763
|
+
}
|
|
4764
|
+
return (0, import_date_fns3.startOfDay)(/* @__PURE__ */ new Date());
|
|
4765
|
+
};
|
|
4766
|
+
var getStatusColor = (status2) => STATUS_META[status2]?.color ?? STATUS_META.pending.color;
|
|
4767
|
+
var getStatusLabel = (status2) => STATUS_META[status2]?.label ?? STATUS_META.pending.label;
|
|
4768
|
+
var ProjectRow = ({ element, barHeight, barSpacing }) => {
|
|
4769
|
+
const safeStartDate = element.startDate instanceof Date ? element.startDate : null;
|
|
4770
|
+
const safeEndDate = element.endDate instanceof Date ? element.endDate : null;
|
|
4771
|
+
const safeStatus = element.status || "pending";
|
|
4772
|
+
const statusColor = getStatusColor(safeStatus);
|
|
4773
|
+
const statusLabel = getStatusLabel(safeStatus);
|
|
4774
|
+
return /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
|
|
4775
|
+
"div",
|
|
4776
|
+
{
|
|
4777
|
+
style: {
|
|
4778
|
+
display: "grid",
|
|
4779
|
+
gridTemplateColumns: "200px 100px 100px 100px",
|
|
4780
|
+
alignItems: "center",
|
|
4781
|
+
height: `${barHeight}px`,
|
|
4782
|
+
fontSize: "12px",
|
|
4783
|
+
marginBottom: `${barSpacing}px`
|
|
4784
|
+
},
|
|
4785
|
+
children: [
|
|
4786
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
|
|
4787
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
|
|
4788
|
+
"span",
|
|
4789
|
+
{
|
|
4790
|
+
style: {
|
|
4791
|
+
display: "inline-block",
|
|
4792
|
+
width: "10px",
|
|
4793
|
+
height: "10px",
|
|
4794
|
+
borderRadius: "50%",
|
|
4795
|
+
backgroundColor: element.color || "#999"
|
|
4796
|
+
}
|
|
4797
|
+
}
|
|
4798
|
+
),
|
|
4799
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)("span", { style: { color: "#333" }, children: element.label })
|
|
4800
|
+
] }),
|
|
4801
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)("div", { style: { color: "#666" }, children: formatThaiDate(safeStartDate) }),
|
|
4802
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)("div", { style: { color: "#666" }, children: formatThaiDate(safeEndDate) }),
|
|
4803
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
|
|
4804
|
+
"button",
|
|
4805
|
+
{
|
|
4806
|
+
style: {
|
|
4807
|
+
height: `${barHeight}px`,
|
|
4808
|
+
minWidth: "100%",
|
|
4809
|
+
padding: "4px 8px",
|
|
4810
|
+
borderRadius: "4px",
|
|
4811
|
+
fontSize: "11px",
|
|
4812
|
+
backgroundColor: statusColor,
|
|
4813
|
+
color: "#000",
|
|
4814
|
+
border: "none",
|
|
4815
|
+
cursor: "pointer"
|
|
4816
|
+
},
|
|
4817
|
+
children: statusLabel
|
|
4818
|
+
}
|
|
4819
|
+
)
|
|
4820
|
+
]
|
|
4821
|
+
},
|
|
4822
|
+
element.id
|
|
4823
|
+
);
|
|
4824
|
+
};
|
|
4825
|
+
var RowOverlay = ({ data, barHeight, barSpacing, totalHeaderHeight }) => /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
|
|
4826
|
+
"div",
|
|
4827
|
+
{
|
|
4828
|
+
style: {
|
|
4829
|
+
position: "absolute",
|
|
4830
|
+
top: `${totalHeaderHeight + 10}px`,
|
|
4831
|
+
left: 0,
|
|
4832
|
+
right: 0,
|
|
4833
|
+
height: `${data.length * (barHeight + barSpacing)}px`,
|
|
4834
|
+
pointerEvents: "none",
|
|
4835
|
+
zIndex: 5
|
|
4836
|
+
},
|
|
4837
|
+
children: data.map((d, i) => {
|
|
4838
|
+
if (i === 0) return null;
|
|
4839
|
+
const yPos = i * (barHeight + barSpacing) - barSpacing / 2;
|
|
4840
|
+
return /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
|
|
4841
|
+
"div",
|
|
4842
|
+
{
|
|
4843
|
+
style: {
|
|
4844
|
+
position: "absolute",
|
|
4845
|
+
top: `${yPos}px`,
|
|
4846
|
+
left: 0,
|
|
4847
|
+
right: 0,
|
|
4848
|
+
height: "1px",
|
|
4849
|
+
backgroundColor: "#d0d0d0"
|
|
4850
|
+
}
|
|
4851
|
+
},
|
|
4852
|
+
`row-line-${d.id}`
|
|
4853
|
+
);
|
|
4854
|
+
})
|
|
4855
|
+
}
|
|
4856
|
+
);
|
|
4857
|
+
var GanttChart = ({ data, width, height }) => {
|
|
4858
|
+
const svgRef = (0, import_react24.useRef)(null);
|
|
4859
|
+
const leftPanelRef = (0, import_react24.useRef)(null);
|
|
4860
|
+
const dataContainerRef = (0, import_react24.useRef)(null);
|
|
4861
|
+
const [viewMode] = (0, import_react24.useState)("year");
|
|
4862
|
+
const { barHeight, barSpacing, headersGroupLayer1Height, headersGroupLayer2Height } = LAYOUT;
|
|
4863
|
+
const totalHeaderHeight = headersGroupLayer1Height + headersGroupLayer2Height;
|
|
4864
|
+
(0, import_react24.useEffect)(() => {
|
|
4865
|
+
if (!data || !svgRef.current) return;
|
|
4866
|
+
const margin = { top: 0, right: 20, bottom: 20, left: 20 };
|
|
4867
|
+
const chartHeight = data.length * (barHeight + barSpacing) + margin.top + margin.bottom;
|
|
4868
|
+
const containerWidth = width * 2;
|
|
4869
|
+
const containerHeight = totalHeaderHeight + chartHeight;
|
|
4870
|
+
const borderRadius = 5;
|
|
4871
|
+
if (leftPanelRef.current) {
|
|
4872
|
+
leftPanelRef.current.style.height = `${containerHeight}px`;
|
|
4873
|
+
}
|
|
4874
|
+
if (dataContainerRef.current) {
|
|
4875
|
+
dataContainerRef.current.style.marginTop = "0px";
|
|
4876
|
+
}
|
|
4877
|
+
const svg = d33.select(svgRef.current).attr("width", containerWidth).attr("height", containerHeight).style("background-color", "white");
|
|
4878
|
+
svg.selectAll("*").remove();
|
|
4879
|
+
const chartGroup = svg.append("g").attr("transform", `translate(${margin.left}, ${totalHeaderHeight})`);
|
|
4880
|
+
const currentYear = (/* @__PURE__ */ new Date()).getFullYear();
|
|
4881
|
+
const domainStart = (0, import_date_fns3.startOfDay)((0, import_date_fns3.startOfYear)(new Date(currentYear, 0, 1)));
|
|
4882
|
+
const domainEnd = (0, import_date_fns3.startOfDay)((0, import_date_fns3.addDays)((0, import_date_fns3.endOfYear)(new Date(currentYear, 0, 1)), 1));
|
|
4883
|
+
const xScale = d33.scaleTime().domain([domainStart, domainEnd]).range([0, containerWidth - margin.left - margin.right]);
|
|
4884
|
+
const buildTimeBuckets = (domain) => ({
|
|
4885
|
+
uniqueYears: d33.timeYear.every(1)?.range(domain[0], domain[1]) || [],
|
|
4886
|
+
uniqueMonths: d33.timeMonth.every(1)?.range(domain[0], domain[1]) || [],
|
|
4887
|
+
uniqueWeeks: (0, import_date_fns3.eachWeekOfInterval)({ start: domain[0], end: domain[1] }, { weekStartsOn: 1 }),
|
|
4888
|
+
uniqueDays: d33.timeDay.range((0, import_date_fns3.startOfDay)(domain[0]), (0, import_date_fns3.startOfDay)((0, import_date_fns3.addDays)(domain[1], 1))) || []
|
|
4889
|
+
});
|
|
4890
|
+
const renderYearHeaders = (layer1, layer2, newXScale, buckets) => {
|
|
4891
|
+
layer1.selectAll(".year-bg").data(buckets.uniqueYears).enter().append("rect").attr("class", "year-bg").attr("x", (d) => newXScale(d)).attr("y", -totalHeaderHeight).attr("width", (d) => {
|
|
4892
|
+
const nextYearStart = (0, import_date_fns3.startOfDay)(new Date(d.getFullYear() + 1, 0, 1));
|
|
4893
|
+
return newXScale(nextYearStart) - newXScale(d);
|
|
4894
|
+
}).attr("height", headersGroupLayer1Height).style("fill", "#fff").style("stroke", "#999").style("stroke-width", 1);
|
|
4895
|
+
layer1.selectAll(".year-label").data(buckets.uniqueYears).enter().append("text").attr("class", "year-label").attr("x", (d) => {
|
|
4896
|
+
const nextYear = new Date(d.getFullYear() + 1, 0, 1);
|
|
4897
|
+
return newXScale(d) + (newXScale(nextYear) - newXScale(d)) / 2;
|
|
4898
|
+
}).attr("y", -totalHeaderHeight + headersGroupLayer1Height / 2).attr("dy", "0.35em").attr("text-anchor", "middle").style("font-size", `${HEADER_FONTS.layer1}px`).style("font-weight", "bold").style("fill", "#0066cc").text((d) => `${d.getFullYear() + 543}`);
|
|
4899
|
+
const quarters2 = [];
|
|
4900
|
+
buckets.uniqueYears.forEach((year) => {
|
|
4901
|
+
for (let q = 0; q < 4; q++) {
|
|
4902
|
+
quarters2.push(new Date(year.getFullYear(), q * 3, 1));
|
|
4903
|
+
}
|
|
4904
|
+
});
|
|
4905
|
+
layer2.selectAll(".quarter-bg").data(quarters2).enter().append("rect").attr("class", "quarter-bg").attr("x", (d) => newXScale(d)).attr("y", -headersGroupLayer2Height).attr("width", (d) => {
|
|
4906
|
+
const nextQuarter = (0, import_date_fns3.startOfDay)(new Date(d.getFullYear(), d.getMonth() + 3, 1));
|
|
4907
|
+
return newXScale(nextQuarter) - newXScale(d);
|
|
4908
|
+
}).attr("height", headersGroupLayer2Height).style("fill", "#fff").style("stroke", "#999").style("stroke-width", 1);
|
|
4909
|
+
layer2.selectAll(".quarter-label").data(quarters2).enter().append("text").attr("class", "quarter-label").attr("x", (d) => {
|
|
4910
|
+
const nextQuarter = (0, import_date_fns3.startOfDay)(new Date(d.getFullYear(), d.getMonth() + 3, 1));
|
|
4911
|
+
return newXScale(d) + (newXScale(nextQuarter) - newXScale(d)) / 2;
|
|
4912
|
+
}).attr("y", -headersGroupLayer2Height / 2).attr("dy", "0.35em").attr("text-anchor", "middle").style("font-size", `${HEADER_FONTS.layer2}px`).style("font-weight", "bold").style("fill", "#0088cc").text((d) => `\u0E44\u0E15\u0E23\u0E21\u0E32\u0E2A ${Math.floor(d.getMonth() / 3) + 1}`);
|
|
4913
|
+
};
|
|
4914
|
+
const renderMonthHeaders = (layer1, layer2, newXScale, buckets) => {
|
|
4915
|
+
layer1.selectAll(".month-bg").data(buckets.uniqueMonths).enter().append("rect").attr("class", "month-bg").attr("x", (d) => newXScale(d)).attr("y", -totalHeaderHeight).attr("width", (d) => {
|
|
4916
|
+
const nextMonth = (0, import_date_fns3.startOfDay)(new Date(d.getFullYear(), d.getMonth() + 1, 1));
|
|
4917
|
+
return newXScale(nextMonth) - newXScale(d) + 11;
|
|
4918
|
+
}).attr("height", headersGroupLayer1Height).style("fill", "#fff").style("stroke", "#999").style("stroke-width", 1);
|
|
4919
|
+
layer1.selectAll(".month-label").data(buckets.uniqueMonths).enter().append("text").attr("class", "month-label").attr("x", (d) => {
|
|
4920
|
+
const nextMonth = (0, import_date_fns3.startOfDay)(new Date(d.getFullYear(), d.getMonth() + 1, 1));
|
|
4921
|
+
return newXScale(d) + (newXScale(nextMonth) - newXScale(d)) / 2;
|
|
4922
|
+
}).attr("y", -totalHeaderHeight + headersGroupLayer1Height / 2).attr("dy", "0.35em").attr("text-anchor", "middle").style("font-size", `${HEADER_FONTS.layer1}px`).style("font-weight", "bold").style("fill", "#0066cc").text((d) => `${formatThaiMonth(d)} ${d.getFullYear() + 543}`);
|
|
4923
|
+
layer2.selectAll(".week-bg").data(buckets.uniqueWeeks).enter().append("rect").attr("class", "week-bg").attr("x", (d) => newXScale(d)).attr("y", -headersGroupLayer2Height).attr("width", (d) => {
|
|
4924
|
+
const weekEnd = (0, import_date_fns3.startOfDay)((0, import_date_fns3.addDays)(d, 7));
|
|
4925
|
+
return newXScale(weekEnd) - newXScale(d) + 1;
|
|
4926
|
+
}).attr("height", headersGroupLayer2Height).style("fill", "#fff").style("stroke", "#999").style("stroke-width", 1);
|
|
4927
|
+
layer2.selectAll(".week-label").data(buckets.uniqueWeeks).enter().append("text").attr("class", "week-label").attr("x", (d) => {
|
|
4928
|
+
const weekEnd = (0, import_date_fns3.startOfDay)((0, import_date_fns3.addDays)(d, 7));
|
|
4929
|
+
return newXScale(d) + (newXScale(weekEnd) - newXScale(d)) / 2;
|
|
4930
|
+
}).attr("y", -headersGroupLayer2Height / 2).attr("dy", "0.35em").attr("text-anchor", "middle").style("font-size", `${HEADER_FONTS.layer2}px`).style("font-weight", "bold").style("fill", "#0088cc").text((d) => `\u0E2A\u0E31\u0E1B\u0E14\u0E32\u0E2B\u0E4C\u0E17\u0E35\u0E48 ${(0, import_date_fns3.getWeek)(d, { weekStartsOn: 1 })}`);
|
|
4931
|
+
};
|
|
4932
|
+
const renderWeekHeaders = (layer1, layer2, newXScale, buckets) => {
|
|
4933
|
+
layer1.selectAll(".week-bg").data(buckets.uniqueWeeks).enter().append("rect").attr("class", "week-bg").attr("x", (d) => newXScale(d)).attr("y", -totalHeaderHeight).attr("width", (d) => {
|
|
4934
|
+
const weekEnd = (0, import_date_fns3.startOfDay)((0, import_date_fns3.addDays)(d, 7));
|
|
4935
|
+
return newXScale(weekEnd) - newXScale(d);
|
|
4936
|
+
}).attr("height", headersGroupLayer1Height).style("fill", "#fff").style("stroke", "#999").style("stroke-width", 1);
|
|
4937
|
+
layer1.selectAll(".week-label").data(buckets.uniqueWeeks).enter().append("text").attr("class", "week-label").attr("x", (d) => {
|
|
4938
|
+
const weekEnd = (0, import_date_fns3.startOfDay)((0, import_date_fns3.addDays)(d, 7));
|
|
4939
|
+
return newXScale(d) + (newXScale(weekEnd) - newXScale(d)) / 2;
|
|
4940
|
+
}).attr("y", -totalHeaderHeight + headersGroupLayer1Height / 2).attr("dy", "0.35em").attr("text-anchor", "middle").style("font-size", `${HEADER_FONTS.layer1}px`).style("font-weight", "bold").style("fill", "#0066cc").text((d) => `\u0E2A\u0E31\u0E1B\u0E14\u0E32\u0E2B\u0E4C\u0E17\u0E35\u0E48 ${(0, import_date_fns3.getWeek)(d, { weekStartsOn: 1 })}`);
|
|
4941
|
+
layer2.selectAll(".day-bg").data(buckets.uniqueDays).enter().append("rect").attr("class", "day-bg").attr("x", (d) => newXScale(d)).attr("y", -headersGroupLayer2Height).attr("width", (d) => {
|
|
4942
|
+
const nextDay = (0, import_date_fns3.startOfDay)((0, import_date_fns3.addDays)(d, 1));
|
|
4943
|
+
return newXScale(nextDay) - newXScale(d);
|
|
4944
|
+
}).attr("height", headersGroupLayer2Height).style("fill", "#fff").style("stroke", "#999").style("stroke-width", 1);
|
|
4945
|
+
layer2.selectAll(".day-of-week").data(buckets.uniqueDays).enter().append("text").attr("class", "day-of-week").attr("x", (d) => {
|
|
4946
|
+
const nextDay = (0, import_date_fns3.startOfDay)((0, import_date_fns3.addDays)(d, 1));
|
|
4947
|
+
return newXScale(d) + (newXScale(nextDay) - newXScale(d)) / 2;
|
|
4948
|
+
}).attr("y", -headersGroupLayer2Height / 2).attr("text-anchor", "middle").style("font-size", `${HEADER_FONTS.layer2}px`).style("font-weight", "bold").style("fill", "#0088cc").each(function(d) {
|
|
4949
|
+
const textElement = d33.select(this);
|
|
4950
|
+
textElement.text("");
|
|
4951
|
+
textElement.append("tspan").text(THAI_DAYS[d.getDay()]).attr("x", textElement.attr("x")).attr("dy", "-0.3em");
|
|
4952
|
+
textElement.append("tspan").text((0, import_date_fns3.format)(d, "d")).attr("x", textElement.attr("x")).attr("dy", "1.2em");
|
|
4953
|
+
});
|
|
4954
|
+
};
|
|
4955
|
+
const drawHeaders = (layer1, layer2, newXScale, buckets) => {
|
|
4956
|
+
if (viewMode === "year") return renderYearHeaders(layer1, layer2, newXScale, buckets);
|
|
4957
|
+
if (viewMode === "month") return renderMonthHeaders(layer1, layer2, newXScale, buckets);
|
|
4958
|
+
return renderWeekHeaders(layer1, layer2, newXScale, buckets);
|
|
4959
|
+
};
|
|
4960
|
+
const getGridlineData = (mode, buckets, domain) => {
|
|
4961
|
+
if (mode === "year") return buckets.uniqueMonths;
|
|
4962
|
+
if (mode === "month") return buckets.uniqueWeeks;
|
|
4963
|
+
if (mode === "week") return buckets.uniqueDays;
|
|
4964
|
+
return (0, import_date_fns3.eachHourOfInterval)({ start: domain[0], end: domain[1] });
|
|
4965
|
+
};
|
|
4966
|
+
const drawGridlines = (group, gridlineData, newXScale) => {
|
|
4967
|
+
group.selectAll(".gridline").data(gridlineData).enter().append("line").attr("class", "gridline").attr("x1", (d) => newXScale(d)).attr("x2", (d) => newXScale(d)).attr("y1", 0).attr("y2", chartHeight - margin.top - margin.bottom).attr("stroke", "#000").attr("stroke-width", 1).style("opacity", 0.3);
|
|
4968
|
+
};
|
|
4969
|
+
const drawRowSeparators = (group, getYPosition) => {
|
|
4970
|
+
const rowSeparatorGroup = group.append("g").attr("class", "row-separators");
|
|
4971
|
+
data.forEach((_d, i) => {
|
|
4972
|
+
if (i === 0) return;
|
|
4973
|
+
const y = getYPosition(i) - barSpacing / 2;
|
|
4974
|
+
rowSeparatorGroup.append("line").attr("x1", 0).attr("x2", containerWidth - margin.left - margin.right).attr("y1", y).attr("y2", y).attr("stroke", "#d0d0d0").attr("stroke-width", 1).style("pointer-events", "none");
|
|
4975
|
+
});
|
|
4976
|
+
rowSeparatorGroup.lower();
|
|
4977
|
+
};
|
|
4978
|
+
const drawBarsAndLabels = (group, newXScale, getYPosition) => {
|
|
4979
|
+
const barsGroup = group.append("g").attr("class", "bars-group");
|
|
4980
|
+
const renderBarHeight = barHeight;
|
|
4981
|
+
const labelPadding = 10;
|
|
4982
|
+
barsGroup.selectAll(".bar-background").data(data).enter().append("rect").attr("class", "bar-background").attr("x", (d) => newXScale(getAdjustedStart(d))).attr("y", (_d, i) => getYPosition(i)).attr("height", renderBarHeight).attr("width", (d) => newXScale(getAdjustedEnd(d)) - newXScale(getAdjustedStart(d))).attr("fill", (d) => getStatusColor(d.status)).attr("rx", borderRadius).attr("ry", borderRadius);
|
|
4983
|
+
barsGroup.selectAll(".bar-head").data(data).enter().append("path").attr("class", "bar-head").attr("fill", (d) => d.color).attr("d", (d, i) => {
|
|
4984
|
+
const x = newXScale(getAdjustedStart(d));
|
|
4985
|
+
const y = getYPosition(i);
|
|
4986
|
+
const barWidth = newXScale(getAdjustedEnd(d)) - newXScale(getAdjustedStart(d));
|
|
4987
|
+
const headWidth = Math.min(barWidth * 0.5, 20);
|
|
4988
|
+
return roundedRectPath(x, y, headWidth, renderBarHeight, borderRadius);
|
|
4989
|
+
});
|
|
4990
|
+
const labels = group.append("g").attr("class", "labels");
|
|
4991
|
+
const labelSelection = labels.selectAll(".label").data(data).enter().append("text").attr("class", "label").attr("y", (_d, i) => getYPosition(i) + renderBarHeight / 2).attr("dy", "0.35em").text((d) => d.label).style("fill", "black").style("font-size", "12px").style("pointer-events", "none");
|
|
4992
|
+
labelSelection.each(function(d) {
|
|
4993
|
+
const barWidth = newXScale(getAdjustedEnd(d)) - newXScale(getAdjustedStart(d));
|
|
4994
|
+
const headWidth = Math.min(barWidth * 0.5, 20);
|
|
4995
|
+
const availableInsideWidth = barWidth - headWidth - labelPadding;
|
|
4996
|
+
const insideX = newXScale(getAdjustedStart(d)) + headWidth + labelPadding;
|
|
4997
|
+
const textElement = d33.select(this);
|
|
4998
|
+
const textWidth = this.getComputedTextLength();
|
|
4999
|
+
if (textWidth > availableInsideWidth) {
|
|
5000
|
+
textElement.attr("x", newXScale(getAdjustedEnd(d)) + labelPadding).attr("data-label-position", "outside");
|
|
5001
|
+
} else {
|
|
5002
|
+
textElement.attr("x", insideX).attr("data-label-position", "inside");
|
|
5003
|
+
}
|
|
5004
|
+
});
|
|
5005
|
+
};
|
|
5006
|
+
const renderChart = (transform) => {
|
|
5007
|
+
const headerMarginBottom = 10;
|
|
5008
|
+
const getYPosition = (index) => headerMarginBottom + index * (barHeight + barSpacing);
|
|
5009
|
+
const newXScale = transform.rescaleX(xScale);
|
|
5010
|
+
chartGroup.selectAll("*").remove();
|
|
5011
|
+
const timeBuckets = buildTimeBuckets(newXScale.domain());
|
|
5012
|
+
const headersGroupLayer1 = chartGroup.append("g");
|
|
5013
|
+
const headersGroupLayer2 = chartGroup.append("g");
|
|
5014
|
+
drawHeaders(headersGroupLayer1, headersGroupLayer2, newXScale, timeBuckets);
|
|
5015
|
+
const gridlineData = getGridlineData(viewMode, timeBuckets, newXScale.domain());
|
|
5016
|
+
drawGridlines(headersGroupLayer2, gridlineData, newXScale);
|
|
5017
|
+
drawRowSeparators(chartGroup, getYPosition);
|
|
5018
|
+
drawBarsAndLabels(chartGroup, newXScale, getYPosition);
|
|
5019
|
+
};
|
|
5020
|
+
const fixedScale = VIEW_MODE_SCALE[viewMode];
|
|
5021
|
+
const dragBehavior = d33.zoom().scaleExtent([fixedScale, fixedScale]).on("zoom", (event) => {
|
|
5022
|
+
const constrainedTransform = d33.zoomIdentity.translate(event.transform.x, event.transform.y).scale(fixedScale);
|
|
5023
|
+
renderChart(constrainedTransform);
|
|
5024
|
+
});
|
|
5025
|
+
svg.call(dragBehavior);
|
|
5026
|
+
const todayPosition = xScale(/* @__PURE__ */ new Date());
|
|
5027
|
+
const translateX = width + leftPanelRef.current.offsetWidth * 1.5 - todayPosition * fixedScale;
|
|
5028
|
+
const initialTransform = d33.zoomIdentity.translate(translateX, 0).scale(fixedScale);
|
|
5029
|
+
svg.call(dragBehavior.transform, initialTransform);
|
|
5030
|
+
}, [
|
|
5031
|
+
data,
|
|
5032
|
+
width,
|
|
5033
|
+
height,
|
|
5034
|
+
viewMode,
|
|
5035
|
+
barHeight,
|
|
5036
|
+
barSpacing,
|
|
5037
|
+
totalHeaderHeight,
|
|
5038
|
+
headersGroupLayer1Height,
|
|
5039
|
+
headersGroupLayer2Height
|
|
5040
|
+
]);
|
|
5041
|
+
return /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
|
|
5042
|
+
"div",
|
|
5043
|
+
{
|
|
5044
|
+
style: {
|
|
5045
|
+
display: "flex",
|
|
5046
|
+
flexDirection: "column",
|
|
5047
|
+
padding: "20px",
|
|
5048
|
+
width: `${width}px`,
|
|
5049
|
+
height: `${height}px`,
|
|
5050
|
+
border: "1px solid #ccc",
|
|
5051
|
+
borderRadius: "10px",
|
|
5052
|
+
backgroundColor: "#fff",
|
|
5053
|
+
overflow: "hidden"
|
|
5054
|
+
},
|
|
5055
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
|
|
5056
|
+
"div",
|
|
5057
|
+
{
|
|
5058
|
+
style: {
|
|
5059
|
+
display: "flex",
|
|
5060
|
+
flex: 1,
|
|
5061
|
+
overflow: "auto",
|
|
5062
|
+
overflowX: "hidden",
|
|
5063
|
+
position: "relative"
|
|
5064
|
+
},
|
|
5065
|
+
children: [
|
|
5066
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)(RowOverlay, { data, barHeight, barSpacing, totalHeaderHeight }),
|
|
5067
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
|
|
5068
|
+
"div",
|
|
5069
|
+
{
|
|
5070
|
+
ref: leftPanelRef,
|
|
5071
|
+
style: {
|
|
5072
|
+
width: "520px",
|
|
5073
|
+
minWidth: "500px",
|
|
5074
|
+
display: "flex",
|
|
5075
|
+
flexDirection: "column",
|
|
5076
|
+
backgroundColor: "#fff",
|
|
5077
|
+
borderRight: "1px solid #ddd",
|
|
5078
|
+
zIndex: 2
|
|
5079
|
+
},
|
|
5080
|
+
children: [
|
|
5081
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
|
|
5082
|
+
"div",
|
|
5083
|
+
{
|
|
5084
|
+
style: {
|
|
5085
|
+
display: "grid",
|
|
5086
|
+
gridTemplateColumns: "200px 100px 100px 100px",
|
|
5087
|
+
fontSize: "12px",
|
|
5088
|
+
fontWeight: "bold",
|
|
5089
|
+
color: "#666",
|
|
5090
|
+
backgroundColor: "#fafafa",
|
|
5091
|
+
borderBottom: "2px solid #ddd",
|
|
5092
|
+
padding: "0 8px",
|
|
5093
|
+
height: `${totalHeaderHeight}px`,
|
|
5094
|
+
marginBottom: "10px",
|
|
5095
|
+
position: "sticky",
|
|
5096
|
+
top: 0,
|
|
5097
|
+
alignItems: "center",
|
|
5098
|
+
boxSizing: "border-box"
|
|
5099
|
+
},
|
|
5100
|
+
children: [
|
|
5101
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)("div", { children: "\u0E42\u0E04\u0E23\u0E07\u0E01\u0E32\u0E23" }),
|
|
5102
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)("div", { children: "\u0E27\u0E31\u0E19\u0E40\u0E23\u0E34\u0E48\u0E21" }),
|
|
5103
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)("div", { children: "\u0E01\u0E33\u0E2B\u0E19\u0E14\u0E2A\u0E48\u0E07" }),
|
|
5104
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)("div", { children: "\u0E2A\u0E16\u0E32\u0E19\u0E30" })
|
|
5105
|
+
]
|
|
5106
|
+
}
|
|
5107
|
+
),
|
|
5108
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)("div", { ref: dataContainerRef, children: data.map((element) => /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ProjectRow, { element, barHeight, barSpacing }, element.id)) })
|
|
5109
|
+
]
|
|
5110
|
+
}
|
|
5111
|
+
),
|
|
5112
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
|
|
5113
|
+
"div",
|
|
5114
|
+
{
|
|
5115
|
+
style: {
|
|
5116
|
+
flex: 1,
|
|
5117
|
+
minWidth: 0,
|
|
5118
|
+
position: "relative",
|
|
5119
|
+
overflow: "visible"
|
|
5120
|
+
},
|
|
5121
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
|
|
5122
|
+
"div",
|
|
5123
|
+
{
|
|
5124
|
+
style: {
|
|
5125
|
+
position: "absolute",
|
|
5126
|
+
right: 0,
|
|
5127
|
+
width: "max-content",
|
|
5128
|
+
zIndex: 1
|
|
5129
|
+
},
|
|
5130
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("svg", { ref: svgRef })
|
|
5131
|
+
}
|
|
5132
|
+
)
|
|
5133
|
+
}
|
|
5134
|
+
)
|
|
5135
|
+
]
|
|
5136
|
+
}
|
|
5137
|
+
)
|
|
5138
|
+
}
|
|
5139
|
+
);
|
|
5140
|
+
};
|
|
4687
5141
|
// Annotate the CommonJS export names for ESM import in node:
|
|
4688
5142
|
0 && (module.exports = {
|
|
4689
5143
|
AntDModal,
|
|
@@ -4700,6 +5154,7 @@ var PieChart = ({
|
|
|
4700
5154
|
DatePickerRange,
|
|
4701
5155
|
FileUploader,
|
|
4702
5156
|
FilterPopUp,
|
|
5157
|
+
GanttChart,
|
|
4703
5158
|
GhostButton,
|
|
4704
5159
|
HeadingPage,
|
|
4705
5160
|
Indicator,
|