@esic-lab/data-core-ui 0.0.54 → 0.0.56

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -357,6 +357,7 @@ var index_exports = {};
357
357
  __export(index_exports, {
358
358
  AntDModal: () => AntDModal,
359
359
  AntDataTable: () => AntDataTable,
360
+ BarChart: () => BarChart,
360
361
  Breadcrumbs: () => Breadcrumbs,
361
362
  Calendar: () => Calendar,
362
363
  Checkbox: () => Checkbox,
@@ -376,6 +377,7 @@ __export(index_exports, {
376
377
  KpiSection: () => KpiSection,
377
378
  Loader: () => Loader,
378
379
  MenuNavBar: () => MenuNavBar,
380
+ PieChart: () => PieChart,
379
381
  PrimaryButton: () => PrimaryButton,
380
382
  ProfileSelect: () => ProfileSelect,
381
383
  ProgressBar: () => ProgressBar,
@@ -1243,7 +1245,7 @@ function InputFieldNumber({
1243
1245
  addonAfter,
1244
1246
  defaultValue,
1245
1247
  className,
1246
- max,
1248
+ max: max2,
1247
1249
  min,
1248
1250
  controls,
1249
1251
  size,
@@ -1276,7 +1278,7 @@ function InputFieldNumber({
1276
1278
  addonBefore,
1277
1279
  addonAfter,
1278
1280
  defaultValue,
1279
- max,
1281
+ max: max2,
1280
1282
  min,
1281
1283
  controls,
1282
1284
  size,
@@ -1730,8 +1732,8 @@ function splitColorStr(str, parseNum) {
1730
1732
  return numList;
1731
1733
  }
1732
1734
  var parseHSVorHSL = (num, _, index) => index === 0 ? num : num / 100;
1733
- function limitRange(value, max) {
1734
- const mergedMax = max || 255;
1735
+ function limitRange(value, max2) {
1736
+ const mergedMax = max2 || 255;
1735
1737
  if (value > mergedMax) {
1736
1738
  return mergedMax;
1737
1739
  }
@@ -2025,9 +2027,9 @@ var FastColor = class _FastColor {
2025
2027
  }
2026
2028
  // ====================== Privates ======================
2027
2029
  /** Return a new FastColor object with one channel changed */
2028
- _sc(rgb, value, max) {
2030
+ _sc(rgb, value, max2) {
2029
2031
  const clone = this.clone();
2030
- clone[rgb] = limitRange(value, max);
2032
+ clone[rgb] = limitRange(value, max2);
2031
2033
  return clone;
2032
2034
  }
2033
2035
  _c(input) {
@@ -4343,7 +4345,7 @@ var QRCodeGenerator = ({
4343
4345
  fileBaseName = "qr-code"
4344
4346
  }) => {
4345
4347
  const canvasRef = (0, import_react21.useRef)(null);
4346
- const [format3, setFormat] = (0, import_react21.useState)("png");
4348
+ const [format4, setFormat] = (0, import_react21.useState)("png");
4347
4349
  const [exportSize, setExportSize] = (0, import_react21.useState)(defaultExportSize);
4348
4350
  const sizeOption = [
4349
4351
  {
@@ -4397,7 +4399,7 @@ var QRCodeGenerator = ({
4397
4399
  }, [url, previewSize]);
4398
4400
  const download = async () => {
4399
4401
  try {
4400
- if (format3 === "svg") {
4402
+ if (format4 === "svg") {
4401
4403
  const svgString = await import_qrcode.default.toString(url, {
4402
4404
  type: "svg",
4403
4405
  width: exportSize,
@@ -4413,9 +4415,9 @@ var QRCodeGenerator = ({
4413
4415
  width: exportSize,
4414
4416
  margin: 1
4415
4417
  });
4416
- const mime = format3 === "png" ? "image/png" : "image/jpeg";
4417
- const dataURL = format3 === "jpeg" ? offscreen.toDataURL(mime, 0.92) : offscreen.toDataURL(mime);
4418
- triggerDownload(dataURL, `${fileBaseName}.${format3}`);
4418
+ const mime = format4 === "png" ? "image/png" : "image/jpeg";
4419
+ const dataURL = format4 === "jpeg" ? offscreen.toDataURL(mime, 0.92) : offscreen.toDataURL(mime);
4420
+ triggerDownload(dataURL, `${fileBaseName}.${format4}`);
4419
4421
  }
4420
4422
  } catch (err) {
4421
4423
  console.error("Failed to generate QR export:", err);
@@ -4448,7 +4450,7 @@ var QRCodeGenerator = ({
4448
4450
  SelectField,
4449
4451
  {
4450
4452
  label: "\u0E23\u0E39\u0E1B\u0E41\u0E1A\u0E1A",
4451
- value: format3,
4453
+ value: format4,
4452
4454
  onChange: (e) => setFormat(e.target.value),
4453
4455
  options: typeOption
4454
4456
  }
@@ -4508,10 +4510,164 @@ function TabProject({ tabOption, now, onChange }) {
4508
4510
  }
4509
4511
  );
4510
4512
  }
4513
+
4514
+ // src/Chart/BarChart/BarChart.tsx
4515
+ var import_react22 = require("react");
4516
+ var d3 = __toESM(require("d3"));
4517
+ var import_jsx_runtime45 = require("react/jsx-runtime");
4518
+ var defaultMargin = { top: 30, right: 200, bottom: 36, left: 50 };
4519
+ var defaultColorPalette = [
4520
+ "#4E79A7",
4521
+ "#F28E2B",
4522
+ "#E15759",
4523
+ "#76B7B2",
4524
+ "#59A14F",
4525
+ "#EDC949",
4526
+ "#AF7AA1",
4527
+ "#FF9DA7",
4528
+ "#9C755F",
4529
+ "#BAB0AC"
4530
+ ];
4531
+ var BarChart = ({
4532
+ data,
4533
+ height = 300,
4534
+ margin = defaultMargin,
4535
+ yLabel,
4536
+ xLabel,
4537
+ colorPalette = defaultColorPalette
4538
+ }) => {
4539
+ const svgRef = (0, import_react22.useRef)(null);
4540
+ const gRef = (0, import_react22.useRef)(null);
4541
+ const xAxisRef = (0, import_react22.useRef)(null);
4542
+ const yAxisRef = (0, import_react22.useRef)(null);
4543
+ const containerRef = (0, import_react22.useRef)(null);
4544
+ const widthRef = (0, import_react22.useRef)(0);
4545
+ (0, import_react22.useEffect)(() => {
4546
+ if (!containerRef.current) return;
4547
+ const ro = new ResizeObserver((entries) => {
4548
+ const cr = entries[0].contentRect;
4549
+ widthRef.current = cr.width;
4550
+ render();
4551
+ });
4552
+ ro.observe(containerRef.current);
4553
+ return () => ro.disconnect();
4554
+ }, []);
4555
+ const xDomain = (0, import_react22.useMemo)(() => data.map((d) => d.x), [data]);
4556
+ const yDomain = (0, import_react22.useMemo)(() => {
4557
+ const maxY = d3.max(data, (d) => d.y);
4558
+ return [0, (maxY !== void 0 ? maxY : 0) + (maxY !== void 0 ? maxY : 0) * 0.1];
4559
+ }, [data]);
4560
+ const render = () => {
4561
+ const svg = d3.select(svgRef.current);
4562
+ const g = d3.select(gRef.current);
4563
+ const xAxisG = d3.select(xAxisRef.current);
4564
+ const yAxisG = d3.select(yAxisRef.current);
4565
+ const width = Math.max(200, widthRef.current || 600);
4566
+ const innerW = width - margin.left - margin.right;
4567
+ const innerH = height - margin.top - margin.bottom;
4568
+ svg.attr("width", width).attr("height", height);
4569
+ const x = d3.scaleBand().domain(xDomain).range([0, innerW]).padding(0.2);
4570
+ const y = d3.scaleLinear().domain(yDomain).nice().range([innerH, 0]);
4571
+ const xAxis = d3.axisBottom(x).tickSizeOuter(0).tickSize(0).tickFormat((d) => d);
4572
+ const yAxis = d3.axisLeft(y).ticks(5).tickFormat(d3.format("~s"));
4573
+ const grid = d3.axisLeft(y).ticks(5).tickSize(-innerW).scale(y);
4574
+ const gridG = svg.append("g").attr("class", "grid").attr("transform", `translate(${margin.left}, ${margin.top})`).call(grid).style("font-size", "14px");
4575
+ gridG.selectAll("path").remove();
4576
+ const barsG = svg.append("g").attr("class", "bars");
4577
+ svg.selectAll(".grid").data([0]).join("g").attr("class", "grid").attr("transform", `translate(${margin.left}, ${margin.top})`).call(grid).style("font-size", "14px").call((g2) => g2.selectAll("path").remove()).call(
4578
+ (g2) => g2.selectAll("line").style("stroke", "gray").style("stroke-opacity", 0.1)
4579
+ // keep faint
4580
+ );
4581
+ svg.select(".grid").lower();
4582
+ xAxisG.attr("transform", `translate(${margin.left},${height - margin.bottom})`).call(xAxis).selectAll("text").style("text-anchor", "middle").attr("dy", "1.5em").attr("fill", "currentColor").style("font-size", "14px").style("font-family", "Arial, sans-serif");
4583
+ yAxisG.selectAll(".y-axis-label").data(yLabel ? [yLabel] : []).join(
4584
+ (enter) => enter.append("text").attr("class", "y-axis-label").attr("fill", "currentColor").attr("x", -margin.left + 8).attr("y", -margin.top + 20).attr("text-anchor", "start").style("font-size", "14px").style("font-family", "Arial").text((d) => d),
4585
+ (update) => update.text((d) => d)
4586
+ );
4587
+ xAxisG.selectAll(".x-axis-label").data(xLabel ? [xLabel] : []).join(
4588
+ (enter) => enter.append("text").attr("class", "x-axis-label").attr("fill", "currentColor").attr("x", width).attr("y", height - margin.bottom).attr("text-anchor", "middle").style("font-size", "14px").style("font-family", "Arial").text((d) => d),
4589
+ (update) => update.text((d) => d).attr("x", width)
4590
+ );
4591
+ const t = svg.transition().duration(400);
4592
+ const bars = g.selectAll("rect.bar").data(data, (d) => d.x);
4593
+ bars.join(
4594
+ (enter) => enter.append("rect").attr("class", "bar").attr("x", (d) => margin.left).attr("width", x.bandwidth()).attr("y", () => margin.top + y(0)).attr("height", () => innerH - y(0)).attr("rx", 5).attr("ry", 5).style("fill", (d, i) => colorPalette[i % colorPalette.length]).append("title").text((d) => `${d.x}: ${d.y}`).merge(bars).attr("width", x.bandwidth()).attr("y", (d) => y(d.y)).attr("height", (d) => innerH - y(d.y)).call((enter2) => enter2.raise()),
4595
+ // Bring the new bars to the front
4596
+ (update) => update.call((update2) => update2.raise()).transition(t).attr("x", (d) => x(d.x) ?? 0).attr("width", x.bandwidth()).attr("y", (d) => y(d.y)).attr("height", (d) => innerH - y(d.y)).style("fill", (d, i) => colorPalette[i % colorPalette.length]),
4597
+ // Update color on update
4598
+ (exit) => exit.transition(t).attr("y", margin.top + y(0)).attr("height", innerH - y(0)).remove()
4599
+ );
4600
+ g.selectAll("text.bar-label").data(data).join(
4601
+ (enter) => enter.append("text").attr("class", "bar-label").attr("x", (d) => (x(d.x) ?? 0) + x.bandwidth() / 2).attr("y", (d) => y(d.y) - 6).attr("text-anchor", "middle").style("font-size", "50px").style("font-weight", "bold").style("font-family", "Arial, sans-serif").style("fill", (d, i) => colorPalette[i % colorPalette.length]).text((d) => d.y),
4602
+ (update) => update.transition(t).attr("x", (d) => (x(d.x) ?? 0) + x.bandwidth() / 2).attr("y", (d) => y(d.y) - 6).text((d) => d.y),
4603
+ (exit) => exit.remove()
4604
+ );
4605
+ };
4606
+ (0, import_react22.useEffect)(() => {
4607
+ render();
4608
+ }, [data, height, margin, xDomain.toString(), yDomain.toString()]);
4609
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { ref: containerRef, style: { width: "100%" }, children: /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("svg", { ref: svgRef, role: "img", "aria-label": "Bar chart", style: { display: "block", width: "100%", height }, children: [
4610
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("g", { ref: gRef, transform: `translate(${margin.left},${margin.top})` }),
4611
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("g", { ref: xAxisRef }),
4612
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("g", { ref: yAxisRef })
4613
+ ] }) });
4614
+ };
4615
+
4616
+ // src/Chart/PieChart/PieChart.tsx
4617
+ var import_react23 = __toESM(require("react"));
4618
+ var d32 = __toESM(require("d3"));
4619
+ var import_jsx_runtime46 = require("react/jsx-runtime");
4620
+ var defaultColors = d32.schemeCategory10;
4621
+ var PieChart = ({
4622
+ title,
4623
+ description,
4624
+ data,
4625
+ width,
4626
+ height,
4627
+ colorPalette = defaultColors
4628
+ }) => {
4629
+ const svgRef = (0, import_react23.useRef)(null);
4630
+ const [dataSide, setDataSide] = import_react23.default.useState([]);
4631
+ (0, import_react23.useEffect)(() => {
4632
+ if (!svgRef.current) return;
4633
+ d32.select(svgRef.current).selectAll("*").remove();
4634
+ const radius = Math.min(width, height) / 2;
4635
+ const totalValue = d32.sum(data, (d) => d.value) ?? 0;
4636
+ const svg = d32.select(svgRef.current).attr("width", width).attr("height", height).append("g").attr("transform", `translate(${width / 2}, ${height / 2})`);
4637
+ const pie2 = d32.pie().value((d) => d.value);
4638
+ const arc2 = d32.arc().innerRadius(0).outerRadius(radius);
4639
+ const arcs = svg.selectAll(".arc").data(pie2(data)).enter().append("g").attr("class", "arc");
4640
+ arcs.append("path").attr("d", arc2).attr("fill", (d, i) => colorPalette[i]).attr("stroke", "white").attr("stroke-width", 2).attr("stroke-linejoin", "round");
4641
+ setDataSide(
4642
+ data.map((d, i) => ({
4643
+ color: colorPalette[i],
4644
+ label: d.label,
4645
+ value: d.value
4646
+ }))
4647
+ );
4648
+ arcs.append("text").attr("transform", (d) => `translate(${arc2.centroid(d)})`).attr("dy", ".35em").attr("text-anchor", "middle").style("fill", "white").style("font-family", "Kanit").style("font-size", "20px").style("font-style", "normal").style("font-weight", "500px").text((d) => {
4649
+ const percentage = (d.data.value / totalValue * 100).toFixed(1);
4650
+ return `${percentage}%`;
4651
+ });
4652
+ }, [data, width, height]);
4653
+ return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { children: [
4654
+ title && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("p", { className: "body-2", children: title }),
4655
+ description && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("p", { className: "caption-1", children: description }),
4656
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "flex", children: [
4657
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("svg", { ref: svgRef }),
4658
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "flex flex-col gap-2 body-3 pl-[200px]", children: dataSide.map((d, i) => /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "grid grid-cols-3 gap-2 items-center", children: [
4659
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "w-[20px] h-[20px]", style: { backgroundColor: d.color } }),
4660
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { children: d.label }),
4661
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { children: d.value })
4662
+ ] }, i)) })
4663
+ ] })
4664
+ ] });
4665
+ };
4511
4666
  // Annotate the CommonJS export names for ESM import in node:
4512
4667
  0 && (module.exports = {
4513
4668
  AntDModal,
4514
4669
  AntDataTable,
4670
+ BarChart,
4515
4671
  Breadcrumbs,
4516
4672
  Calendar,
4517
4673
  Checkbox,
@@ -4531,6 +4687,7 @@ function TabProject({ tabOption, now, onChange }) {
4531
4687
  KpiSection,
4532
4688
  Loader,
4533
4689
  MenuNavBar,
4690
+ PieChart,
4534
4691
  PrimaryButton,
4535
4692
  ProfileSelect,
4536
4693
  ProgressBar,
package/dist/index.mjs CHANGED
@@ -1182,7 +1182,7 @@ function InputFieldNumber({
1182
1182
  addonAfter,
1183
1183
  defaultValue,
1184
1184
  className,
1185
- max,
1185
+ max: max2,
1186
1186
  min,
1187
1187
  controls,
1188
1188
  size,
@@ -1215,7 +1215,7 @@ function InputFieldNumber({
1215
1215
  addonBefore,
1216
1216
  addonAfter,
1217
1217
  defaultValue,
1218
- max,
1218
+ max: max2,
1219
1219
  min,
1220
1220
  controls,
1221
1221
  size,
@@ -1669,8 +1669,8 @@ function splitColorStr(str, parseNum) {
1669
1669
  return numList;
1670
1670
  }
1671
1671
  var parseHSVorHSL = (num, _, index) => index === 0 ? num : num / 100;
1672
- function limitRange(value, max) {
1673
- const mergedMax = max || 255;
1672
+ function limitRange(value, max2) {
1673
+ const mergedMax = max2 || 255;
1674
1674
  if (value > mergedMax) {
1675
1675
  return mergedMax;
1676
1676
  }
@@ -1964,9 +1964,9 @@ var FastColor = class _FastColor {
1964
1964
  }
1965
1965
  // ====================== Privates ======================
1966
1966
  /** Return a new FastColor object with one channel changed */
1967
- _sc(rgb, value, max) {
1967
+ _sc(rgb, value, max2) {
1968
1968
  const clone = this.clone();
1969
- clone[rgb] = limitRange(value, max);
1969
+ clone[rgb] = limitRange(value, max2);
1970
1970
  return clone;
1971
1971
  }
1972
1972
  _c(input) {
@@ -4288,7 +4288,7 @@ var QRCodeGenerator = ({
4288
4288
  fileBaseName = "qr-code"
4289
4289
  }) => {
4290
4290
  const canvasRef = useRef7(null);
4291
- const [format3, setFormat] = useState19("png");
4291
+ const [format4, setFormat] = useState19("png");
4292
4292
  const [exportSize, setExportSize] = useState19(defaultExportSize);
4293
4293
  const sizeOption = [
4294
4294
  {
@@ -4342,7 +4342,7 @@ var QRCodeGenerator = ({
4342
4342
  }, [url, previewSize]);
4343
4343
  const download = async () => {
4344
4344
  try {
4345
- if (format3 === "svg") {
4345
+ if (format4 === "svg") {
4346
4346
  const svgString = await QRCode.toString(url, {
4347
4347
  type: "svg",
4348
4348
  width: exportSize,
@@ -4358,9 +4358,9 @@ var QRCodeGenerator = ({
4358
4358
  width: exportSize,
4359
4359
  margin: 1
4360
4360
  });
4361
- const mime = format3 === "png" ? "image/png" : "image/jpeg";
4362
- const dataURL = format3 === "jpeg" ? offscreen.toDataURL(mime, 0.92) : offscreen.toDataURL(mime);
4363
- triggerDownload(dataURL, `${fileBaseName}.${format3}`);
4361
+ const mime = format4 === "png" ? "image/png" : "image/jpeg";
4362
+ const dataURL = format4 === "jpeg" ? offscreen.toDataURL(mime, 0.92) : offscreen.toDataURL(mime);
4363
+ triggerDownload(dataURL, `${fileBaseName}.${format4}`);
4364
4364
  }
4365
4365
  } catch (err) {
4366
4366
  console.error("Failed to generate QR export:", err);
@@ -4393,7 +4393,7 @@ var QRCodeGenerator = ({
4393
4393
  SelectField,
4394
4394
  {
4395
4395
  label: "\u0E23\u0E39\u0E1B\u0E41\u0E1A\u0E1A",
4396
- value: format3,
4396
+ value: format4,
4397
4397
  onChange: (e) => setFormat(e.target.value),
4398
4398
  options: typeOption
4399
4399
  }
@@ -4453,9 +4453,163 @@ function TabProject({ tabOption, now, onChange }) {
4453
4453
  }
4454
4454
  );
4455
4455
  }
4456
+
4457
+ // src/Chart/BarChart/BarChart.tsx
4458
+ import { useEffect as useEffect9, useMemo as useMemo2, useRef as useRef8 } from "react";
4459
+ import * as d3 from "d3";
4460
+ import { jsx as jsx45, jsxs as jsxs39 } from "react/jsx-runtime";
4461
+ var defaultMargin = { top: 30, right: 200, bottom: 36, left: 50 };
4462
+ var defaultColorPalette = [
4463
+ "#4E79A7",
4464
+ "#F28E2B",
4465
+ "#E15759",
4466
+ "#76B7B2",
4467
+ "#59A14F",
4468
+ "#EDC949",
4469
+ "#AF7AA1",
4470
+ "#FF9DA7",
4471
+ "#9C755F",
4472
+ "#BAB0AC"
4473
+ ];
4474
+ var BarChart = ({
4475
+ data,
4476
+ height = 300,
4477
+ margin = defaultMargin,
4478
+ yLabel,
4479
+ xLabel,
4480
+ colorPalette = defaultColorPalette
4481
+ }) => {
4482
+ const svgRef = useRef8(null);
4483
+ const gRef = useRef8(null);
4484
+ const xAxisRef = useRef8(null);
4485
+ const yAxisRef = useRef8(null);
4486
+ const containerRef = useRef8(null);
4487
+ const widthRef = useRef8(0);
4488
+ useEffect9(() => {
4489
+ if (!containerRef.current) return;
4490
+ const ro = new ResizeObserver((entries) => {
4491
+ const cr = entries[0].contentRect;
4492
+ widthRef.current = cr.width;
4493
+ render();
4494
+ });
4495
+ ro.observe(containerRef.current);
4496
+ return () => ro.disconnect();
4497
+ }, []);
4498
+ const xDomain = useMemo2(() => data.map((d) => d.x), [data]);
4499
+ const yDomain = useMemo2(() => {
4500
+ const maxY = d3.max(data, (d) => d.y);
4501
+ return [0, (maxY !== void 0 ? maxY : 0) + (maxY !== void 0 ? maxY : 0) * 0.1];
4502
+ }, [data]);
4503
+ const render = () => {
4504
+ const svg = d3.select(svgRef.current);
4505
+ const g = d3.select(gRef.current);
4506
+ const xAxisG = d3.select(xAxisRef.current);
4507
+ const yAxisG = d3.select(yAxisRef.current);
4508
+ const width = Math.max(200, widthRef.current || 600);
4509
+ const innerW = width - margin.left - margin.right;
4510
+ const innerH = height - margin.top - margin.bottom;
4511
+ svg.attr("width", width).attr("height", height);
4512
+ const x = d3.scaleBand().domain(xDomain).range([0, innerW]).padding(0.2);
4513
+ const y = d3.scaleLinear().domain(yDomain).nice().range([innerH, 0]);
4514
+ const xAxis = d3.axisBottom(x).tickSizeOuter(0).tickSize(0).tickFormat((d) => d);
4515
+ const yAxis = d3.axisLeft(y).ticks(5).tickFormat(d3.format("~s"));
4516
+ const grid = d3.axisLeft(y).ticks(5).tickSize(-innerW).scale(y);
4517
+ const gridG = svg.append("g").attr("class", "grid").attr("transform", `translate(${margin.left}, ${margin.top})`).call(grid).style("font-size", "14px");
4518
+ gridG.selectAll("path").remove();
4519
+ const barsG = svg.append("g").attr("class", "bars");
4520
+ svg.selectAll(".grid").data([0]).join("g").attr("class", "grid").attr("transform", `translate(${margin.left}, ${margin.top})`).call(grid).style("font-size", "14px").call((g2) => g2.selectAll("path").remove()).call(
4521
+ (g2) => g2.selectAll("line").style("stroke", "gray").style("stroke-opacity", 0.1)
4522
+ // keep faint
4523
+ );
4524
+ svg.select(".grid").lower();
4525
+ xAxisG.attr("transform", `translate(${margin.left},${height - margin.bottom})`).call(xAxis).selectAll("text").style("text-anchor", "middle").attr("dy", "1.5em").attr("fill", "currentColor").style("font-size", "14px").style("font-family", "Arial, sans-serif");
4526
+ yAxisG.selectAll(".y-axis-label").data(yLabel ? [yLabel] : []).join(
4527
+ (enter) => enter.append("text").attr("class", "y-axis-label").attr("fill", "currentColor").attr("x", -margin.left + 8).attr("y", -margin.top + 20).attr("text-anchor", "start").style("font-size", "14px").style("font-family", "Arial").text((d) => d),
4528
+ (update) => update.text((d) => d)
4529
+ );
4530
+ xAxisG.selectAll(".x-axis-label").data(xLabel ? [xLabel] : []).join(
4531
+ (enter) => enter.append("text").attr("class", "x-axis-label").attr("fill", "currentColor").attr("x", width).attr("y", height - margin.bottom).attr("text-anchor", "middle").style("font-size", "14px").style("font-family", "Arial").text((d) => d),
4532
+ (update) => update.text((d) => d).attr("x", width)
4533
+ );
4534
+ const t = svg.transition().duration(400);
4535
+ const bars = g.selectAll("rect.bar").data(data, (d) => d.x);
4536
+ bars.join(
4537
+ (enter) => enter.append("rect").attr("class", "bar").attr("x", (d) => margin.left).attr("width", x.bandwidth()).attr("y", () => margin.top + y(0)).attr("height", () => innerH - y(0)).attr("rx", 5).attr("ry", 5).style("fill", (d, i) => colorPalette[i % colorPalette.length]).append("title").text((d) => `${d.x}: ${d.y}`).merge(bars).attr("width", x.bandwidth()).attr("y", (d) => y(d.y)).attr("height", (d) => innerH - y(d.y)).call((enter2) => enter2.raise()),
4538
+ // Bring the new bars to the front
4539
+ (update) => update.call((update2) => update2.raise()).transition(t).attr("x", (d) => x(d.x) ?? 0).attr("width", x.bandwidth()).attr("y", (d) => y(d.y)).attr("height", (d) => innerH - y(d.y)).style("fill", (d, i) => colorPalette[i % colorPalette.length]),
4540
+ // Update color on update
4541
+ (exit) => exit.transition(t).attr("y", margin.top + y(0)).attr("height", innerH - y(0)).remove()
4542
+ );
4543
+ g.selectAll("text.bar-label").data(data).join(
4544
+ (enter) => enter.append("text").attr("class", "bar-label").attr("x", (d) => (x(d.x) ?? 0) + x.bandwidth() / 2).attr("y", (d) => y(d.y) - 6).attr("text-anchor", "middle").style("font-size", "50px").style("font-weight", "bold").style("font-family", "Arial, sans-serif").style("fill", (d, i) => colorPalette[i % colorPalette.length]).text((d) => d.y),
4545
+ (update) => update.transition(t).attr("x", (d) => (x(d.x) ?? 0) + x.bandwidth() / 2).attr("y", (d) => y(d.y) - 6).text((d) => d.y),
4546
+ (exit) => exit.remove()
4547
+ );
4548
+ };
4549
+ useEffect9(() => {
4550
+ render();
4551
+ }, [data, height, margin, xDomain.toString(), yDomain.toString()]);
4552
+ return /* @__PURE__ */ jsx45("div", { ref: containerRef, style: { width: "100%" }, children: /* @__PURE__ */ jsxs39("svg", { ref: svgRef, role: "img", "aria-label": "Bar chart", style: { display: "block", width: "100%", height }, children: [
4553
+ /* @__PURE__ */ jsx45("g", { ref: gRef, transform: `translate(${margin.left},${margin.top})` }),
4554
+ /* @__PURE__ */ jsx45("g", { ref: xAxisRef }),
4555
+ /* @__PURE__ */ jsx45("g", { ref: yAxisRef })
4556
+ ] }) });
4557
+ };
4558
+
4559
+ // src/Chart/PieChart/PieChart.tsx
4560
+ import React3, { useRef as useRef9, useEffect as useEffect10 } from "react";
4561
+ import * as d32 from "d3";
4562
+ import { jsx as jsx46, jsxs as jsxs40 } from "react/jsx-runtime";
4563
+ var defaultColors = d32.schemeCategory10;
4564
+ var PieChart = ({
4565
+ title,
4566
+ description,
4567
+ data,
4568
+ width,
4569
+ height,
4570
+ colorPalette = defaultColors
4571
+ }) => {
4572
+ const svgRef = useRef9(null);
4573
+ const [dataSide, setDataSide] = React3.useState([]);
4574
+ useEffect10(() => {
4575
+ if (!svgRef.current) return;
4576
+ d32.select(svgRef.current).selectAll("*").remove();
4577
+ const radius = Math.min(width, height) / 2;
4578
+ const totalValue = d32.sum(data, (d) => d.value) ?? 0;
4579
+ const svg = d32.select(svgRef.current).attr("width", width).attr("height", height).append("g").attr("transform", `translate(${width / 2}, ${height / 2})`);
4580
+ const pie2 = d32.pie().value((d) => d.value);
4581
+ const arc2 = d32.arc().innerRadius(0).outerRadius(radius);
4582
+ const arcs = svg.selectAll(".arc").data(pie2(data)).enter().append("g").attr("class", "arc");
4583
+ arcs.append("path").attr("d", arc2).attr("fill", (d, i) => colorPalette[i]).attr("stroke", "white").attr("stroke-width", 2).attr("stroke-linejoin", "round");
4584
+ setDataSide(
4585
+ data.map((d, i) => ({
4586
+ color: colorPalette[i],
4587
+ label: d.label,
4588
+ value: d.value
4589
+ }))
4590
+ );
4591
+ arcs.append("text").attr("transform", (d) => `translate(${arc2.centroid(d)})`).attr("dy", ".35em").attr("text-anchor", "middle").style("fill", "white").style("font-family", "Kanit").style("font-size", "20px").style("font-style", "normal").style("font-weight", "500px").text((d) => {
4592
+ const percentage = (d.data.value / totalValue * 100).toFixed(1);
4593
+ return `${percentage}%`;
4594
+ });
4595
+ }, [data, width, height]);
4596
+ return /* @__PURE__ */ jsxs40("div", { children: [
4597
+ title && /* @__PURE__ */ jsx46("p", { className: "body-2", children: title }),
4598
+ description && /* @__PURE__ */ jsx46("p", { className: "caption-1", children: description }),
4599
+ /* @__PURE__ */ jsxs40("div", { className: "flex", children: [
4600
+ /* @__PURE__ */ jsx46("svg", { ref: svgRef }),
4601
+ /* @__PURE__ */ jsx46("div", { className: "flex flex-col gap-2 body-3 pl-[200px]", children: dataSide.map((d, i) => /* @__PURE__ */ jsxs40("div", { className: "grid grid-cols-3 gap-2 items-center", children: [
4602
+ /* @__PURE__ */ jsx46("div", { className: "w-[20px] h-[20px]", style: { backgroundColor: d.color } }),
4603
+ /* @__PURE__ */ jsx46("div", { children: d.label }),
4604
+ /* @__PURE__ */ jsx46("div", { children: d.value })
4605
+ ] }, i)) })
4606
+ ] })
4607
+ ] });
4608
+ };
4456
4609
  export {
4457
4610
  AntDModal,
4458
4611
  AntDataTable,
4612
+ BarChart,
4459
4613
  Breadcrumbs,
4460
4614
  Calendar,
4461
4615
  Checkbox,
@@ -4475,6 +4629,7 @@ export {
4475
4629
  KpiSection,
4476
4630
  Loader,
4477
4631
  MenuNavBar,
4632
+ PieChart,
4478
4633
  PrimaryButton,
4479
4634
  ProfileSelect,
4480
4635
  ProgressBar,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@esic-lab/data-core-ui",
3
- "version": "0.0.54",
3
+ "version": "0.0.56",
4
4
  "main": "dist/index.js",
5
5
  "module": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -41,8 +41,10 @@
41
41
  "test": "echo \"Error: no test specified\" && exit 1",
42
42
  "storybook": "storybook dev -p 6006",
43
43
  "build-storybook": "storybook build",
44
- "build": "tsup src/index.ts --dts --dts-resolve --format esm,cjs --out-dir dist && npm run copy-assets",
45
- "copy-assets": "xcopy src\\assets dist\\assets /E /I /Y"
44
+ "build": "tsup src/index.ts --format esm,cjs --out-dir dist && npm run copy-assets",
45
+ "build:dts": "tsup src/index.ts --dts --dts-resolve --out-dir dist",
46
+ "copy-assets": "xcopy src\\assets dist\\assets /E /I /Y",
47
+ "type-check": "tsc --noEmit"
46
48
  },
47
49
  "peerDependencies": {
48
50
  "react": ">=18.0.0",