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

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.d.ts CHANGED
@@ -7,7 +7,6 @@ import { InputNumberProps, SelectProps } from 'antd';
7
7
  import { Color } from 'antd/es/color-picker';
8
8
  import { MessageInstance } from 'antd/es/message/interface';
9
9
  import { ItemType } from 'antd/es/breadcrumb/Breadcrumb';
10
- import { ReactNode as ReactNode$1 } from '@tabler/icons-react';
11
10
 
12
11
  type ColorScale = 50 | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900;
13
12
  type BaseColor = "primary" | "gray" | "green" | "red" | "yellow" | "blue";
@@ -443,10 +442,9 @@ interface SortFilterProps {
443
442
  declare function SortFilter({ showYear, showQuarter, showMonth, onSortClick, onFilterClick, }: SortFilterProps): react_jsx_runtime.JSX.Element;
444
443
 
445
444
  interface FileUploaderProps {
446
- onUpload: (file: any) => Promise<void>;
445
+ onUpload: (file: File) => Promise<void>;
447
446
  onRemove?: (index: number) => Promise<void>;
448
447
  onError?: (message: string) => void;
449
- onClickFile?: (file: any) => void;
450
448
  accept?: string[];
451
449
  maxSize?: number;
452
450
  disabled?: boolean;
@@ -454,12 +452,8 @@ interface FileUploaderProps {
454
452
  description?: string;
455
453
  label?: string;
456
454
  value?: File[] | any[];
457
- uploadText?: string;
458
- uploaderWidth?: string;
459
- attachWidth?: string;
460
- readOnly?: boolean;
461
455
  }
462
- declare function FileUploader({ onUpload, onError, onRemove, onClickFile, accept, maxSize, disabled, mode, description, label, value, uploadText, uploaderWidth, attachWidth, readOnly, }: FileUploaderProps): react_jsx_runtime.JSX.Element;
456
+ declare function FileUploader({ onUpload, onError, onRemove, accept, maxSize, disabled, mode, description, label, value, }: FileUploaderProps): react_jsx_runtime.JSX.Element;
463
457
 
464
458
  declare function setMessageApi(api: MessageInstance): void;
465
459
  declare function messageSuccess(content: string): void;
@@ -528,22 +522,16 @@ interface IndicatorProps {
528
522
  }[];
529
523
  type: "OUTPUT" | "OUTCOME";
530
524
  arrayData: IndicatorArray[];
531
- canEdit?: boolean;
532
525
  setArrayData: (data: IndicatorArray[]) => void;
533
- onDeleteClick?: (payload: {
534
- index: number;
535
- item: IndicatorArray;
536
- confirm: () => void;
537
- }) => void;
538
526
  }
539
527
  interface IndicatorArray {
540
528
  indicatorType: "OUTPUT" | "OUTCOME";
541
529
  inputType: "TEXT" | "NUMBER";
542
530
  textValue: string;
543
- numberValue?: number;
531
+ numberValue?: string;
544
532
  unit?: string;
545
533
  }
546
- declare function Indicator({ option, type, arrayData, setArrayData, canEdit, onDeleteClick, }: IndicatorProps): react_jsx_runtime.JSX.Element;
534
+ declare function Indicator({ option, type, arrayData, setArrayData, }: IndicatorProps): react_jsx_runtime.JSX.Element;
547
535
 
548
536
  interface optionSelect {
549
537
  value: string;
@@ -554,38 +542,4 @@ declare const FilterPopUp: (filter: {
554
542
  handleSearch: (listFiler: string[]) => void;
555
543
  }) => react_jsx_runtime.JSX.Element;
556
544
 
557
- interface UserData {
558
- id: string;
559
- name: string;
560
- profile: string;
561
- }
562
- interface ProfileSelectProp {
563
- allUser: UserData[];
564
- assignUser: UserData[];
565
- mode: "show" | "showAssign" | "icon";
566
- className?: string;
567
- onUpdateAssignUser: (user: UserData, action?: "add" | "remove") => void;
568
- }
569
- declare function ProfileSelect({ allUser, assignUser, mode, className, onUpdateAssignUser, }: ProfileSelectProp): react_jsx_runtime.JSX.Element;
570
-
571
- interface QRCodeGeneratorProps {
572
- url: string;
573
- previewSize?: number;
574
- defaultExportSize?: number;
575
- fileBaseName?: string;
576
- }
577
- declare const QRCodeGenerator: react.FC<QRCodeGeneratorProps>;
578
-
579
- interface TabProjectProp {
580
- tabOption: {
581
- key: string;
582
- label: string;
583
- icon: ReactNode$1;
584
- }[];
585
- projectId: string;
586
- now: string;
587
- onChange: (key: string) => void;
588
- }
589
- declare function TabProject({ tabOption, now, onChange }: TabProjectProp): react_jsx_runtime.JSX.Element;
590
-
591
- export { AntDModal, AntDataTable, Breadcrumbs, Calendar, Checkbox, CheckboxGroup, ColorPalettePickerBasic, ColorPickerBasic, DataTable, DatePickerBasic, DatePickerRange, FileUploader, FilterPopUp, GhostButton, HeadingPage, Indicator, InputField, InputFieldNumber, KpiSection, Loader, MenuNavBar, type MenuNavBarProps, PrimaryButton, ProfileSelect, ProgressBar, QRCodeGenerator, Radio, RadioGroup, SecondaryButton, SelectCustom, SelectField, SelectFieldGroup, SelectFieldStatus, SelectFieldStatusReport, SelectFieldTag, Sidebar, SortFilter, Switch, SwitchSelect, TabProject, TabSelectionButton, TextAreaInput, TextInput, TopNavBar, messageError, messageInfo, messageLoading, messageSuccess, messageWarning, setMessageApi };
545
+ export { AntDModal, AntDataTable, Breadcrumbs, Calendar, Checkbox, CheckboxGroup, ColorPalettePickerBasic, ColorPickerBasic, DataTable, DatePickerBasic, DatePickerRange, FileUploader, FilterPopUp, GhostButton, HeadingPage, Indicator, InputField, InputFieldNumber, KpiSection, Loader, MenuNavBar, type MenuNavBarProps, PrimaryButton, ProgressBar, Radio, RadioGroup, SecondaryButton, SelectCustom, SelectField, SelectFieldGroup, SelectFieldStatus, SelectFieldStatusReport, SelectFieldTag, Sidebar, SortFilter, Switch, SwitchSelect, TabSelectionButton, TextAreaInput, TextInput, TopNavBar, messageError, messageInfo, messageLoading, messageSuccess, messageWarning, setMessageApi };
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,165 @@ 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)("title", { children: "Bar chart" }),
4611
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("g", { ref: gRef, transform: `translate(${margin.left},${margin.top})` }),
4612
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("g", { ref: xAxisRef }),
4613
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("g", { ref: yAxisRef })
4614
+ ] }) });
4615
+ };
4616
+
4617
+ // src/Chart/PieChart/PieChart.tsx
4618
+ var import_react23 = __toESM(require("react"));
4619
+ var d32 = __toESM(require("d3"));
4620
+ var import_jsx_runtime46 = require("react/jsx-runtime");
4621
+ var defaultColors = d32.schemeCategory10;
4622
+ var PieChart = ({
4623
+ title,
4624
+ description,
4625
+ data,
4626
+ width,
4627
+ height,
4628
+ colorPalette = defaultColors
4629
+ }) => {
4630
+ const svgRef = (0, import_react23.useRef)(null);
4631
+ const [dataSide, setDataSide] = import_react23.default.useState([]);
4632
+ (0, import_react23.useEffect)(() => {
4633
+ if (!svgRef.current) return;
4634
+ d32.select(svgRef.current).selectAll("*").remove();
4635
+ const radius = Math.min(width, height) / 2;
4636
+ const totalValue = d32.sum(data, (d) => d.value) ?? 0;
4637
+ const svg = d32.select(svgRef.current).attr("width", width).attr("height", height).append("g").attr("transform", `translate(${width / 2}, ${height / 2})`);
4638
+ const pie2 = d32.pie().value((d) => d.value);
4639
+ const arc2 = d32.arc().innerRadius(0).outerRadius(radius);
4640
+ const arcs = svg.selectAll(".arc").data(pie2(data)).enter().append("g").attr("class", "arc");
4641
+ arcs.append("path").attr("d", arc2).attr("fill", (d, i) => colorPalette[i]).attr("stroke", "white").attr("stroke-width", 2).attr("stroke-linejoin", "round");
4642
+ setDataSide(
4643
+ data.map((d, i) => ({
4644
+ color: colorPalette[i],
4645
+ label: d.label,
4646
+ value: d.value
4647
+ }))
4648
+ );
4649
+ 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) => {
4650
+ const percentage = (d.data.value / totalValue * 100).toFixed(1);
4651
+ return `${percentage}%`;
4652
+ });
4653
+ }, [data, width, height]);
4654
+ return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { children: [
4655
+ title && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("p", { className: "body-2", children: title }),
4656
+ description && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("p", { className: "caption-1", children: description }),
4657
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "flex", children: [
4658
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("svg", { ref: svgRef }),
4659
+ /* @__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: [
4660
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "w-[20px] h-[20px]", style: { backgroundColor: d.color } }),
4661
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { children: d.label }),
4662
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { children: d.value })
4663
+ ] }, i)) })
4664
+ ] })
4665
+ ] });
4666
+ };
4511
4667
  // Annotate the CommonJS export names for ESM import in node:
4512
4668
  0 && (module.exports = {
4513
4669
  AntDModal,
4514
4670
  AntDataTable,
4671
+ BarChart,
4515
4672
  Breadcrumbs,
4516
4673
  Calendar,
4517
4674
  Checkbox,
@@ -4531,6 +4688,7 @@ function TabProject({ tabOption, now, onChange }) {
4531
4688
  KpiSection,
4532
4689
  Loader,
4533
4690
  MenuNavBar,
4691
+ PieChart,
4534
4692
  PrimaryButton,
4535
4693
  ProfileSelect,
4536
4694
  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,164 @@ 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("title", { children: "Bar chart" }),
4554
+ /* @__PURE__ */ jsx45("g", { ref: gRef, transform: `translate(${margin.left},${margin.top})` }),
4555
+ /* @__PURE__ */ jsx45("g", { ref: xAxisRef }),
4556
+ /* @__PURE__ */ jsx45("g", { ref: yAxisRef })
4557
+ ] }) });
4558
+ };
4559
+
4560
+ // src/Chart/PieChart/PieChart.tsx
4561
+ import React3, { useRef as useRef9, useEffect as useEffect10 } from "react";
4562
+ import * as d32 from "d3";
4563
+ import { jsx as jsx46, jsxs as jsxs40 } from "react/jsx-runtime";
4564
+ var defaultColors = d32.schemeCategory10;
4565
+ var PieChart = ({
4566
+ title,
4567
+ description,
4568
+ data,
4569
+ width,
4570
+ height,
4571
+ colorPalette = defaultColors
4572
+ }) => {
4573
+ const svgRef = useRef9(null);
4574
+ const [dataSide, setDataSide] = React3.useState([]);
4575
+ useEffect10(() => {
4576
+ if (!svgRef.current) return;
4577
+ d32.select(svgRef.current).selectAll("*").remove();
4578
+ const radius = Math.min(width, height) / 2;
4579
+ const totalValue = d32.sum(data, (d) => d.value) ?? 0;
4580
+ const svg = d32.select(svgRef.current).attr("width", width).attr("height", height).append("g").attr("transform", `translate(${width / 2}, ${height / 2})`);
4581
+ const pie2 = d32.pie().value((d) => d.value);
4582
+ const arc2 = d32.arc().innerRadius(0).outerRadius(radius);
4583
+ const arcs = svg.selectAll(".arc").data(pie2(data)).enter().append("g").attr("class", "arc");
4584
+ arcs.append("path").attr("d", arc2).attr("fill", (d, i) => colorPalette[i]).attr("stroke", "white").attr("stroke-width", 2).attr("stroke-linejoin", "round");
4585
+ setDataSide(
4586
+ data.map((d, i) => ({
4587
+ color: colorPalette[i],
4588
+ label: d.label,
4589
+ value: d.value
4590
+ }))
4591
+ );
4592
+ 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) => {
4593
+ const percentage = (d.data.value / totalValue * 100).toFixed(1);
4594
+ return `${percentage}%`;
4595
+ });
4596
+ }, [data, width, height]);
4597
+ return /* @__PURE__ */ jsxs40("div", { children: [
4598
+ title && /* @__PURE__ */ jsx46("p", { className: "body-2", children: title }),
4599
+ description && /* @__PURE__ */ jsx46("p", { className: "caption-1", children: description }),
4600
+ /* @__PURE__ */ jsxs40("div", { className: "flex", children: [
4601
+ /* @__PURE__ */ jsx46("svg", { ref: svgRef }),
4602
+ /* @__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: [
4603
+ /* @__PURE__ */ jsx46("div", { className: "w-[20px] h-[20px]", style: { backgroundColor: d.color } }),
4604
+ /* @__PURE__ */ jsx46("div", { children: d.label }),
4605
+ /* @__PURE__ */ jsx46("div", { children: d.value })
4606
+ ] }, i)) })
4607
+ ] })
4608
+ ] });
4609
+ };
4456
4610
  export {
4457
4611
  AntDModal,
4458
4612
  AntDataTable,
4613
+ BarChart,
4459
4614
  Breadcrumbs,
4460
4615
  Calendar,
4461
4616
  Checkbox,
@@ -4475,6 +4630,7 @@ export {
4475
4630
  KpiSection,
4476
4631
  Loader,
4477
4632
  MenuNavBar,
4633
+ PieChart,
4478
4634
  PrimaryButton,
4479
4635
  ProfileSelect,
4480
4636
  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.55",
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",