@adminlte/react 0.6.1 → 0.7.0

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/CHANGELOG.md CHANGED
@@ -5,6 +5,66 @@ All notable changes to **@adminlte/react** are documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.7.0] - 2026-10-01
9
+
10
+ ### Changed — breaking
11
+
12
+ - **Charts now use [Chart.js](https://www.chartjs.org/) 4.5 (MIT) instead of ApexCharts.** From 5.2.0 ApexCharts is distributed under the proprietary *ApexCharts License*, which requires an OEM licence for any product that redistributes it — which is what a template library does. Chart.js is MIT, like everything else this package depends on.
13
+ - **`ApexChart` is removed; use the new `Chart` component.** It takes a Chart.js `type`, `data` and `options` (plus optional `plugins`, `height` — default `300` px —, `ariaLabel`, `fallback`, `className`, `style`). `data`/`options` are plain values, so a Server Component can pass them (callbacks such as tooltip formatters need a client component, as before).
14
+ - **`SparklineChart` keeps its props** (`id`, `data`) and gains optional `color` (default `#DCE6EC`), `type` (`'line' | 'bar'`), `height` (default `50`) and `ariaLabel`.
15
+ - **Peer dependency `apexcharts` → `chart.js` (`>=4.5.0`, optional).** Install `chart.js` if you use `Chart`/`SparklineChart`; drop `apexcharts` and its `apexcharts.css` link — Chart.js needs no stylesheet.
16
+ - Why 0.7.0 and not 1.0.0: the package is pre-1.0, where the minor version is the breaking-change channel (a `^0.6.x` range will not pick up 0.7.0), and nothing else in the public API changed.
17
+
18
+ ### Added
19
+
20
+ - **AdminLTE theme preset for Chart.js** — `applyChartTheme(Chart)`, with `readChartThemeColors()` and `withAlpha()` helpers, exported from the package and applied automatically by `Chart`. It writes `Chart.defaults` from Bootstrap's CSS variables (`--bs-body-font-family`, `--bs-body-color`, `--bs-secondary-color`, `--bs-border-color-translucent`, `--bs-body-bg`, `--bs-emphasis-color`): horizontal gridlines only, no axis borders, rounded bars, smooth lines, circular point-style legends and Bootstrap-style tooltips. A filled line dataset without its own `backgroundColor` gets a vertical gradient of its line colour. Call it yourself if you create Chart.js charts outside the component.
21
+ - **Live dark mode and RTL.** Mounted charts re-read the theme and re-render when `<html data-bs-theme>` or `<html dir>` changes (one shared `MutationObserver`), so the colour-mode toggle restyles axes, legends and tooltips immediately; with `dir="rtl"` legends and tooltips render right-to-left.
22
+
23
+ ### Fixed
24
+
25
+ - Docs → Plugins listed the jsVectorMap stylesheet as `jsvectormap/dist/css/jsvectormap.min.css`, a path that does not exist in jsvectormap 1.7; it is `jsvectormap/dist/jsvectormap.min.css`.
26
+ - Dashboard v2/v3 charts were hard to read in dark mode (dark tick labels and light row stripes on a dark card); the preset now takes its colours from the active colour mode.
27
+
28
+ ### Migration
29
+
30
+ ```tsx
31
+ // 0.6.x
32
+ <ApexChart
33
+ id="sales"
34
+ series={[{ name: 'Sales', data: [30, 40, 35, 50] }]}
35
+ config={{ chart: { type: 'area', height: 300 }, colors: ['#0d6efd'], xaxis: { categories: ['Jan', 'Feb', 'Mar', 'Apr'] } }}
36
+ />
37
+
38
+ // 0.7.0
39
+ <Chart
40
+ id="sales"
41
+ type="line"
42
+ height={300}
43
+ data={{
44
+ labels: ['Jan', 'Feb', 'Mar', 'Apr'],
45
+ datasets: [{ label: 'Sales', data: [30, 40, 35, 50], borderColor: '#0d6efd', fill: 'origin' }],
46
+ }}
47
+ />
48
+ ```
49
+
50
+ | ApexCharts | Chart.js |
51
+ |---|---|
52
+ | `chart.type: 'area'` | `type="line"` + dataset `fill: 'origin'` |
53
+ | `chart.type: 'bar'` (`plotOptions.bar.horizontal`) | `type="bar"` (`options.indexAxis: 'y'`) |
54
+ | `chart.type: 'donut'` / `'pie'` / `'radar'` / `'polarArea'` | `type="doughnut"` / `"pie"` / `"radar"` / `"polarArea"` |
55
+ | `series[i].name` / `.data` | `data.datasets[i].label` / `.data` |
56
+ | `xaxis.categories` / `labels` | `data.labels` |
57
+ | `colors` | per-dataset `borderColor` / `backgroundColor` |
58
+ | `chart.height` | `height` prop |
59
+ | `legend.show: false` | `options.plugins.legend.display: false` |
60
+ | `tooltip.y.formatter` | `options.plugins.tooltip.callbacks.label` |
61
+
62
+ ### Notes
63
+
64
+ - The demo's chart code shrank: the lazily-loaded chart chunk is **68.3 kB gzipped (Chart.js) vs 275.0 kB (ApexCharts)**; all demo JS 587.8 kB vs 789.1 kB gzipped.
65
+ - Every chart on the three dashboards was re-created with the same data and series: Dashboard v1 area chart + 3 sparklines, Dashboard v2 area chart, donut and 7 table sparklines, Dashboard v3 line and grouped bar charts.
66
+ - Verified: library type-check, lint (0 errors, 13 pre-existing warnings), 23 unit tests (4 new for the theme preset) and build (35 client modules in `src/` and `dist/`); demo type-check, production build and the `EXPORT=true` subpath export; all 67 Playwright route-smoke + a11y tests; and a Playwright crawl of all 64 exported routes at `/themes/next-react/` in light and dark mode, plus the chart pages in RTL — no console or page errors, every chart canvas painted.
67
+
8
68
  ## [0.6.1] - 2026-08-19
9
69
 
10
70
  ### Fixed
@@ -240,6 +300,7 @@ Next.js App Router and React Server Components.
240
300
  - Heavy third-party libraries are never bundled eagerly — they load via dynamic
241
301
  `import()` inside the components that use them.
242
302
 
303
+ [0.7.0]: https://github.com/ColorlibHQ/adminlte-react/compare/v0.6.1...v0.7.0
243
304
  [0.6.1]: https://github.com/ColorlibHQ/adminlte-react/compare/v0.6.0...v0.6.1
244
305
  [0.6.0]: https://github.com/ColorlibHQ/adminlte-react/compare/v0.5.0...v0.6.0
245
306
  [0.5.0]: https://github.com/ColorlibHQ/adminlte-react/compare/v0.4.0...v0.5.0
package/README.md CHANGED
@@ -9,7 +9,7 @@
9
9
 
10
10
  A modern React/Next.js component library for building admin dashboards and web applications with AdminLTE 4 styling and Bootstrap 5.3 — built for the Next.js App Router and React Server Components by [Colorlib](https://colorlib.com).
11
11
 
12
- **Status**: v0.6.0 (Early Release, AdminLTE core 4.8.1) · **Live demo:** [adminlte.io/themes/next-react](https://adminlte.io/themes/next-react/)
12
+ **Status**: v0.7.0 (Early Release, AdminLTE core 4.8.1) · **Live demo:** [adminlte.io/themes/next-react](https://adminlte.io/themes/next-react/)
13
13
 
14
14
  <p align="center">
15
15
  <a href="https://adminlte.io/themes/next-react/">
@@ -58,7 +58,7 @@ A modern React/Next.js component library for building admin dashboards and web a
58
58
  - ♿ **Accessible** - Semantic markup, ARIA landmarks, keyboard navigation
59
59
  - 🎯 **TypeScript First** - Fully typed props, hooks, and the menu data model
60
60
  - 📱 **Responsive** - Mobile-first Bootstrap 5.3 design with an off-canvas sidebar
61
- - 🔌 **Lazy plugins** - ApexCharts, Tabulator, Quill, Flatpickr, Tom Select & jsVectorMap load on demand
61
+ - 🔌 **Lazy plugins** - Chart.js, Tabulator, Quill, Flatpickr, Tom Select & jsVectorMap load on demand
62
62
 
63
63
  ## Installation
64
64
 
@@ -89,7 +89,7 @@ npm install bootstrap bootstrap-icons
89
89
  Install only the libraries for the lazy components you use:
90
90
 
91
91
  ```bash
92
- npm install apexcharts # <ApexChart>, <SparklineChart>
92
+ npm install chart.js # <Chart>, <SparklineChart>
93
93
  npm install tabulator-tables # <Datatable>
94
94
  npm install quill # <Editor>
95
95
  npm install flatpickr # <InputFlatpickr>
@@ -250,7 +250,7 @@ export default function Dashboard() {
250
250
  - **`Timeline`** - Chronological timeline
251
251
  - **`NavMessages`** / **`NavNotifications`** / **`NavTasks`** - Topbar dropdown widgets
252
252
  - **`DirectChat`** - Chat widget with contacts pane
253
- - **`ApexChart`** / **`SparklineChart`** - ApexCharts wrappers (dynamic import)
253
+ - **`Chart`** / **`SparklineChart`** - [Chart.js](https://www.chartjs.org/) charts (MIT, dynamic import) with the AdminLTE theme preset (`applyChartTheme`) applied — follows dark mode and RTL live
254
254
  - **`WorldMap`** - jsVectorMap world map (dynamic import)
255
255
  - **`CommandPalette`** - ⌘K searchable navigator (+ `flattenMenuToCommands` helper)
256
256
  - **`Accordion`** / **`Tabs`** - Collapsible panels & tab/pill switcher (state-driven, no Bootstrap JS)
@@ -471,6 +471,8 @@ Need advanced features, more pages, and dedicated support? Explore Colorlib's co
471
471
 
472
472
  MIT © [Colorlib](https://colorlib.com)
473
473
 
474
+ Charts are drawn with [Chart.js](https://www.chartjs.org/) (MIT licence), an optional peer dependency you install yourself — every chart-related dependency of this package is MIT-licensed.
475
+
474
476
  ## Resources
475
477
 
476
478
  - [AdminLTE Documentation](https://adminlte.io)
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as react from 'react';
2
- import react__default, { AnchorHTMLAttributes, ReactNode, Key } from 'react';
3
- import { ApexOptions } from 'apexcharts';
2
+ import react__default, { AnchorHTMLAttributes, ReactNode, CSSProperties, Key } from 'react';
3
+ import { ChartType, DefaultDataPoint, ChartData, ChartOptions, Plugin, Chart as Chart$1 } from 'chart.js';
4
4
 
5
5
  /**
6
6
  * Bootstrap color theme variants.
@@ -457,18 +457,93 @@ interface DirectChatProps {
457
457
  }
458
458
  declare function DirectChat({ badge, contacts, messages, collapsible, removable, onRemove, className, }: DirectChatProps): react.JSX.Element | null;
459
459
 
460
- interface ApexChartProps {
461
- id: string;
462
- series: NonNullable<ApexOptions['series']>;
463
- config: ApexOptions;
460
+ interface ChartProps<TType extends ChartType = ChartType, TData = DefaultDataPoint<TType>, TLabel = unknown> {
461
+ /** DOM id of the chart container. */
462
+ id?: string;
463
+ /** Chart.js chart type: `line`, `bar`, `doughnut`, `pie`, `radar`, `polarArea`, `scatter`, `bubble`… */
464
+ type: TType;
465
+ /** Chart.js `data` (labels + datasets). Plain data, so it can come from a Server Component. */
466
+ data: ChartData<TType, TData, TLabel>;
467
+ /** Chart.js `options`. Merged over the AdminLTE theme preset (`Chart.defaults`). */
468
+ options?: ChartOptions<TType>;
469
+ /** Inline Chart.js plugins for this chart only. */
470
+ plugins?: Plugin<TType>[];
471
+ /** Height of the chart area — a number is pixels. Default `300`. */
472
+ height?: number | string;
473
+ /** Accessible name for the chart (`aria-label` on the canvas). */
474
+ ariaLabel?: string;
475
+ /** Fallback content inside `<canvas>` (e.g. a data table for screen readers). */
476
+ fallback?: ReactNode;
477
+ className?: string;
478
+ style?: CSSProperties;
464
479
  }
465
- declare function ApexChart({ id, series, config }: ApexChartProps): react.JSX.Element;
480
+ /**
481
+ * Chart.js chart, lazy-loaded. `chart.js` is an optional peer dependency — install it
482
+ * (`npm install chart.js`); it is imported on mount, so pages without charts never load it.
483
+ *
484
+ * The AdminLTE theme preset (`applyChartTheme`) is applied automatically and follows the
485
+ * colour mode and document direction live. Charts resize with their container and are
486
+ * destroyed on unmount.
487
+ */
488
+ declare function Chart<TType extends ChartType = ChartType, TData = DefaultDataPoint<TType>, TLabel = unknown>({ id, type, data, options, plugins, height, ariaLabel, fallback, className, style, }: ChartProps<TType, TData, TLabel>): react.JSX.Element;
466
489
 
467
490
  interface SparklineChartProps {
468
491
  id: string;
469
492
  data: number[];
493
+ /** Line (and area) colour. Default `#DCE6EC`. */
494
+ color?: string;
495
+ /** `line` draws a filled area sparkline (default); `bar` draws mini columns. */
496
+ type?: 'line' | 'bar';
497
+ /** Height in pixels. Default `50`. */
498
+ height?: number;
499
+ /** Accessible name for the sparkline. */
500
+ ariaLabel?: string;
470
501
  }
471
- declare function SparklineChart({ id, data }: SparklineChartProps): react.JSX.Element;
502
+ /**
503
+ * Tiny axis-less trend chart (Chart.js, lazy-loaded — install `chart.js`).
504
+ */
505
+ declare function SparklineChart({ id, data, color, type, height, ariaLabel, }: SparklineChartProps): react.JSX.Element;
506
+
507
+ /**
508
+ * AdminLTE theme preset for Chart.js.
509
+ *
510
+ * `applyChartTheme(Chart)` writes the AdminLTE look into `Chart.defaults`: the body font,
511
+ * colours read from Bootstrap's CSS variables (so light/dark mode and custom themes follow),
512
+ * subtle horizontal gridlines only, rounded bars, smooth lines, circular point-style legends
513
+ * and Bootstrap-style tooltips. It is idempotent — `<Chart>` calls it again whenever
514
+ * `<html data-bs-theme>` or `<html dir>` changes, then updates every live chart.
515
+ *
516
+ * It takes the Chart.js constructor as an argument (no static import), so this module stays
517
+ * safe to import from Server Components and never pulls `chart.js` into a bundle by itself.
518
+ */
519
+ type ChartStatic = typeof Chart$1;
520
+ interface ChartThemeColors {
521
+ /** Body font family (`--bs-body-font-family`). */
522
+ fontFamily: string;
523
+ /** Primary text colour (`--bs-body-color`) — legend labels. */
524
+ text: string;
525
+ /** Muted text colour (`--bs-secondary-color`) — axis ticks. */
526
+ muted: string;
527
+ /** Gridline / border colour (`--bs-border-color-translucent`). */
528
+ grid: string;
529
+ /** Surface colour (`--bs-body-bg`) — doughnut segment separators, point borders. */
530
+ surface: string;
531
+ /** Tooltip background (`--bs-emphasis-color`). */
532
+ tooltipBg: string;
533
+ /** Tooltip text (`--bs-body-bg`). */
534
+ tooltipText: string;
535
+ /** `true` when the document is right-to-left. */
536
+ rtl: boolean;
537
+ }
538
+ /** Reads the current theme colours from the document's Bootstrap CSS variables. */
539
+ declare function readChartThemeColors(root?: Element): ChartThemeColors;
540
+ /**
541
+ * Returns `color` with the given alpha. Accepts `#rgb`, `#rrggbb`, `#rrggbbaa`, `rgb()` and
542
+ * `rgba()`; anything else (named colours, gradients) is returned unchanged.
543
+ */
544
+ declare function withAlpha(color: string, alpha: number): string;
545
+ /** Applies the AdminLTE preset to `Chart.defaults`. Safe to call repeatedly. */
546
+ declare function applyChartTheme(Chart: ChartStatic, colors?: ChartThemeColors): void;
472
547
 
473
548
  interface WorldMapProps {
474
549
  id: string;
@@ -1006,4 +1081,4 @@ declare function useTreeviewAnimation(isOpen: boolean, speed?: number): UseTreev
1006
1081
 
1007
1082
  declare function useSortable(enabled?: boolean): void;
1008
1083
 
1009
- export { Accordion, type AccordionItem, type AccordionProps, Alert, ApexChart, AppContent, type AppContentProps, AuthLayout, type AuthLayoutProps, Avatar, AvatarGroup, type AvatarGroupProps, type AvatarProps, type AvatarStatus, Badge, type BadgeProps, type BootstrapTheme, Breadcrumb, type BreadcrumbItem, type BreadcrumbProps, type BreakpointSize, Button, Callout, Card, Carousel, type CarouselProps, type CarouselSlide, type ColorMode, ColorModeProvider, ColorModeToggle, type CommandItem, CommandPalette, CommandPaletteProvider, type ComponentSize, DashboardLayout, type DashboardLayoutProps, Datatable, DescriptionBlock, DirectChat, type DirectChatContact, type DirectChatMessage, Dropdown, type DropdownItem, type DropdownProps, Editor, Footer, InfoBox, Input, InputColor, InputFile, InputFlatpickr, InputSwitch, InputTomSelect, type LinkComponent, type LinkProps, LinkProvider, ListGroup, ListGroupItem, type ListGroupItemProps, type ListGroupProps, type MenuGroup, type MenuHeader, type MenuItem, type MenuNode, Modal, NavMessages, NavNotifications, NavTasks, Offcanvas, type OffcanvasProps, OffcanvasTrigger, type OffcanvasTriggerProps, Pagination, type PaginationProps, ProfileCard, Progress, ProgressGroup, Ratings, Select, Sidebar, SidebarBrand, SidebarNav, SidebarNavItem, SidebarOverlay, SidebarProvider, SmallBox, SparklineChart, Spinner, type SpinnerProps, type Step, Stepper, type StepperProps, type TabItem, Table, type TableColumn, type TableProps, Tabs, type TabsProps, Textarea, Timeline, Toast, type ToastProps, Tooltip, type TooltipProps, Topbar, type TopbarUser, WorldMap, flattenMenuToCommands, useCardWidget, useColorModeContext, useCommandPalette, useDirectChat, useFullscreen, useLinkComponent, usePushMenu, useSidebarContext, useSortable, useTreeviewAnimation };
1084
+ export { Accordion, type AccordionItem, type AccordionProps, Alert, AppContent, type AppContentProps, AuthLayout, type AuthLayoutProps, Avatar, AvatarGroup, type AvatarGroupProps, type AvatarProps, type AvatarStatus, Badge, type BadgeProps, type BootstrapTheme, Breadcrumb, type BreadcrumbItem, type BreadcrumbProps, type BreakpointSize, Button, Callout, Card, Carousel, type CarouselProps, type CarouselSlide, Chart, type ChartProps, type ChartThemeColors, type ColorMode, ColorModeProvider, ColorModeToggle, type CommandItem, CommandPalette, CommandPaletteProvider, type ComponentSize, DashboardLayout, type DashboardLayoutProps, Datatable, DescriptionBlock, DirectChat, type DirectChatContact, type DirectChatMessage, Dropdown, type DropdownItem, type DropdownProps, Editor, Footer, InfoBox, Input, InputColor, InputFile, InputFlatpickr, InputSwitch, InputTomSelect, type LinkComponent, type LinkProps, LinkProvider, ListGroup, ListGroupItem, type ListGroupItemProps, type ListGroupProps, type MenuGroup, type MenuHeader, type MenuItem, type MenuNode, Modal, NavMessages, NavNotifications, NavTasks, Offcanvas, type OffcanvasProps, OffcanvasTrigger, type OffcanvasTriggerProps, Pagination, type PaginationProps, ProfileCard, Progress, ProgressGroup, Ratings, Select, Sidebar, SidebarBrand, SidebarNav, SidebarNavItem, SidebarOverlay, SidebarProvider, SmallBox, SparklineChart, type SparklineChartProps, Spinner, type SpinnerProps, type Step, Stepper, type StepperProps, type TabItem, Table, type TableColumn, type TableProps, Tabs, type TabsProps, Textarea, Timeline, Toast, type ToastProps, Tooltip, type TooltipProps, Topbar, type TopbarUser, WorldMap, applyChartTheme, flattenMenuToCommands, readChartThemeColors, useCardWidget, useColorModeContext, useCommandPalette, useDirectChat, useFullscreen, useLinkComponent, usePushMenu, useSidebarContext, useSortable, useTreeviewAnimation, withAlpha };
package/dist/index.js CHANGED
@@ -28,8 +28,9 @@ import { NavMessages } from "./widget/nav-messages.js";
28
28
  import { NavNotifications } from "./widget/nav-notifications.js";
29
29
  import { NavTasks } from "./widget/nav-tasks.js";
30
30
  import { DirectChat } from "./widget/direct-chat.js";
31
- import { ApexChart } from "./widget/apex-chart.js";
31
+ import { Chart } from "./widget/chart.js";
32
32
  import { SparklineChart } from "./widget/sparkline-chart.js";
33
+ import { applyChartTheme, readChartThemeColors, withAlpha } from "./lib/chart-theme.js";
33
34
  import { WorldMap } from "./widget/world-map.js";
34
35
  import { CommandPalette } from "./widget/command-palette.js";
35
36
  import { flattenMenuToCommands } from "./lib/flatten-menu.js";
@@ -69,7 +70,6 @@ import { useSortable } from "./hooks/use-sortable.js";
69
70
  export {
70
71
  Accordion,
71
72
  Alert,
72
- ApexChart,
73
73
  AppContent,
74
74
  AuthLayout,
75
75
  Avatar,
@@ -80,6 +80,7 @@ export {
80
80
  Callout,
81
81
  Card,
82
82
  Carousel,
83
+ Chart,
83
84
  ColorModeProvider,
84
85
  ColorModeToggle,
85
86
  CommandPalette,
@@ -131,7 +132,9 @@ export {
131
132
  Tooltip,
132
133
  Topbar,
133
134
  WorldMap,
135
+ applyChartTheme,
134
136
  flattenMenuToCommands,
137
+ readChartThemeColors,
135
138
  useCardWidget,
136
139
  useColorModeContext,
137
140
  useCommandPalette,
@@ -141,5 +144,6 @@ export {
141
144
  usePushMenu,
142
145
  useSidebarContext,
143
146
  useSortable,
144
- useTreeviewAnimation
147
+ useTreeviewAnimation,
148
+ withAlpha
145
149
  };
@@ -0,0 +1,120 @@
1
+ const FALLBACK = {
2
+ fontFamily: '"Source Sans 3", system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
3
+ text: "#212529",
4
+ muted: "rgba(33, 37, 41, 0.75)",
5
+ grid: "rgba(0, 0, 0, 0.175)",
6
+ surface: "#fff",
7
+ tooltipBg: "#000",
8
+ tooltipText: "#fff",
9
+ rtl: false
10
+ };
11
+ function readChartThemeColors(root) {
12
+ if (typeof window === "undefined" || typeof document === "undefined") return FALLBACK;
13
+ const el = root ?? document.documentElement;
14
+ const css = getComputedStyle(el);
15
+ const v = (name, fallback) => css.getPropertyValue(name).trim() || fallback;
16
+ return {
17
+ fontFamily: v("--bs-body-font-family", FALLBACK.fontFamily),
18
+ text: v("--bs-body-color", FALLBACK.text),
19
+ muted: v("--bs-secondary-color", FALLBACK.muted),
20
+ grid: v("--bs-border-color-translucent", FALLBACK.grid),
21
+ surface: v("--bs-body-bg", FALLBACK.surface),
22
+ tooltipBg: v("--bs-emphasis-color", FALLBACK.tooltipBg),
23
+ tooltipText: v("--bs-body-bg", FALLBACK.tooltipText),
24
+ rtl: (document.documentElement.getAttribute("dir") || css.direction) === "rtl"
25
+ };
26
+ }
27
+ function withAlpha(color, alpha) {
28
+ const c = color.trim();
29
+ const hex = /^#([0-9a-f]{3,8})$/i.exec(c);
30
+ if (hex) {
31
+ let h = hex[1];
32
+ if (h.length === 3 || h.length === 4) h = h.split("").map((x) => x + x).join("");
33
+ if (h.length !== 6 && h.length !== 8) return color;
34
+ const n = (i) => parseInt(h.slice(i, i + 2), 16);
35
+ return `rgba(${n(0)}, ${n(2)}, ${n(4)}, ${alpha})`;
36
+ }
37
+ const rgb = /^rgba?\(\s*([\d.]+)[\s,]+([\d.]+)[\s,]+([\d.]+)/i.exec(c);
38
+ if (rgb) return `rgba(${rgb[1]}, ${rgb[2]}, ${rgb[3]}, ${alpha})`;
39
+ return color;
40
+ }
41
+ function lineBackground(ctx) {
42
+ const raw = ctx.dataset?.borderColor;
43
+ const base = typeof raw === "string" ? raw : void 0;
44
+ if (!base) return "rgba(0, 0, 0, 0.1)";
45
+ if (ctx.type !== "dataset" || !ctx.dataset.fill) return base;
46
+ const { chart } = ctx;
47
+ const area = chart.chartArea;
48
+ if (!area) return withAlpha(base, 0.2);
49
+ const gradient = chart.ctx.createLinearGradient(0, area.top, 0, area.bottom);
50
+ gradient.addColorStop(0, withAlpha(base, 0.35));
51
+ gradient.addColorStop(1, withAlpha(base, 0.02));
52
+ return gradient;
53
+ }
54
+ function applyChartTheme(Chart, colors = readChartThemeColors()) {
55
+ const d = Chart.defaults;
56
+ d.font.family = colors.fontFamily;
57
+ d.font.size = 12;
58
+ d.color = colors.muted;
59
+ d.borderColor = colors.grid;
60
+ const scale = d.scale;
61
+ scale.grid.color = colors.grid;
62
+ scale.grid.drawTicks = false;
63
+ scale.border.display = false;
64
+ scale.ticks.padding = 8;
65
+ scale.ticks.color = colors.muted;
66
+ const category = d.scales.category;
67
+ if (category) category.grid = { ...category.grid || {}, display: false };
68
+ d.elements.line.tension = 0.4;
69
+ d.elements.line.borderWidth = 2.5;
70
+ d.elements.line.borderCapStyle = "round";
71
+ d.elements.point.radius = 0;
72
+ d.elements.point.hoverRadius = 5;
73
+ d.elements.point.hitRadius = 12;
74
+ d.elements.point.borderWidth = 2;
75
+ d.elements.point.hoverBorderWidth = 2;
76
+ d.elements.point.pointStyle = "circle";
77
+ d.elements.bar.borderRadius = 4;
78
+ d.elements.bar.borderSkipped = "start";
79
+ d.elements.arc.borderColor = colors.surface;
80
+ d.elements.arc.borderWidth = 2;
81
+ d.elements.arc.hoverOffset = 6;
82
+ const lineDefaults = d.datasets.line;
83
+ if (lineDefaults) {
84
+ lineDefaults.backgroundColor = lineBackground;
85
+ lineDefaults.pointHoverBackgroundColor = colors.surface;
86
+ }
87
+ const barDefaults = d.datasets.bar;
88
+ if (barDefaults) barDefaults.maxBarThickness = 28;
89
+ for (const type of ["line", "bar"]) {
90
+ const o = Chart.overrides[type];
91
+ if (o) o.interaction = { mode: "index", intersect: false };
92
+ }
93
+ const legend = d.plugins.legend;
94
+ legend.rtl = colors.rtl;
95
+ legend.labels.color = colors.text;
96
+ legend.labels.usePointStyle = true;
97
+ legend.labels.pointStyle = "circle";
98
+ legend.labels.boxWidth = 8;
99
+ legend.labels.boxHeight = 8;
100
+ legend.labels.padding = 16;
101
+ const tooltip = d.plugins.tooltip;
102
+ tooltip.rtl = colors.rtl;
103
+ tooltip.backgroundColor = withAlpha(colors.tooltipBg, 0.9);
104
+ tooltip.titleColor = colors.tooltipText;
105
+ tooltip.bodyColor = colors.tooltipText;
106
+ tooltip.footerColor = colors.tooltipText;
107
+ tooltip.borderWidth = 0;
108
+ tooltip.cornerRadius = 6;
109
+ tooltip.padding = 10;
110
+ tooltip.boxPadding = 4;
111
+ tooltip.caretSize = 5;
112
+ tooltip.usePointStyle = true;
113
+ tooltip.titleFont = { weight: "bold" };
114
+ tooltip.titleMarginBottom = 6;
115
+ }
116
+ export {
117
+ applyChartTheme,
118
+ readChartThemeColors,
119
+ withAlpha
120
+ };
@@ -0,0 +1,125 @@
1
+ "use client";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import { useEffect, useRef } from "react";
4
+ import { applyChartTheme } from "../lib/chart-theme.js";
5
+ const liveCharts = /* @__PURE__ */ new Map();
6
+ let ChartClass = null;
7
+ let observer = null;
8
+ function retheme() {
9
+ if (!ChartClass) return;
10
+ applyChartTheme(ChartClass);
11
+ liveCharts.forEach((freshOptions, chart) => {
12
+ chart.options = freshOptions();
13
+ chart.update("none");
14
+ });
15
+ }
16
+ function track(chart, freshOptions) {
17
+ liveCharts.set(chart, freshOptions);
18
+ if (!observer && typeof MutationObserver !== "undefined") {
19
+ observer = new MutationObserver(retheme);
20
+ observer.observe(document.documentElement, {
21
+ attributes: true,
22
+ attributeFilter: ["data-bs-theme", "dir"]
23
+ });
24
+ }
25
+ }
26
+ function untrack(chart) {
27
+ liveCharts.delete(chart);
28
+ if (liveCharts.size === 0 && observer) {
29
+ observer.disconnect();
30
+ observer = null;
31
+ }
32
+ }
33
+ async function loadChart() {
34
+ if (!ChartClass) {
35
+ const mod = await import("chart.js/auto");
36
+ ChartClass = mod.default;
37
+ }
38
+ applyChartTheme(ChartClass);
39
+ return ChartClass;
40
+ }
41
+ function cloneData(data) {
42
+ try {
43
+ return structuredClone(data);
44
+ } catch {
45
+ return data;
46
+ }
47
+ }
48
+ function cloneOptions(value) {
49
+ if (Array.isArray(value)) return value.map(cloneOptions);
50
+ if (value && typeof value === "object" && Object.getPrototypeOf(value) === Object.prototype) {
51
+ const out = {};
52
+ for (const [k, v] of Object.entries(value)) out[k] = cloneOptions(v);
53
+ return out;
54
+ }
55
+ return value;
56
+ }
57
+ function withBaseOptions(options) {
58
+ return { responsive: true, maintainAspectRatio: false, ...cloneOptions(options || {}) };
59
+ }
60
+ function Chart({
61
+ id,
62
+ type,
63
+ data,
64
+ options,
65
+ plugins,
66
+ height = 300,
67
+ ariaLabel,
68
+ fallback,
69
+ className,
70
+ style
71
+ }) {
72
+ const canvasRef = useRef(null);
73
+ const chartRef = useRef(null);
74
+ const optionsRef = useRef(options);
75
+ useEffect(() => {
76
+ optionsRef.current = options;
77
+ });
78
+ const dataKey = JSON.stringify(data);
79
+ const optionsKey = JSON.stringify(options ?? {});
80
+ useEffect(() => {
81
+ let cancelled = false;
82
+ const canvas = canvasRef.current;
83
+ if (!canvas) return;
84
+ loadChart().then((ChartCtor) => {
85
+ if (cancelled || !canvasRef.current) return;
86
+ ChartCtor.getChart(canvas)?.destroy();
87
+ const chart = new ChartCtor(canvas, {
88
+ type,
89
+ data: cloneData(data),
90
+ options: withBaseOptions(options),
91
+ plugins
92
+ });
93
+ chartRef.current = chart;
94
+ track(chart, () => withBaseOptions(optionsRef.current));
95
+ }).catch((err) => console.error("Chart init error:", err));
96
+ return () => {
97
+ cancelled = true;
98
+ const chart = chartRef.current;
99
+ if (chart) {
100
+ untrack(chart);
101
+ chart.destroy();
102
+ chartRef.current = null;
103
+ }
104
+ };
105
+ }, [type]);
106
+ useEffect(() => {
107
+ const chart = chartRef.current;
108
+ if (!chart) return;
109
+ chart.data = cloneData(data);
110
+ chart.options = withBaseOptions(options);
111
+ chart.update();
112
+ }, [dataKey, optionsKey]);
113
+ return /* @__PURE__ */ jsx(
114
+ "div",
115
+ {
116
+ id,
117
+ className,
118
+ style: { position: "relative", height: typeof height === "number" ? `${height}px` : height, ...style },
119
+ children: /* @__PURE__ */ jsx("canvas", { ref: canvasRef, role: "img", "aria-label": ariaLabel ?? `${type} chart`, children: fallback })
120
+ }
121
+ );
122
+ }
123
+ export {
124
+ Chart
125
+ };
@@ -1,42 +1,53 @@
1
1
  "use client";
2
2
  import { jsx } from "react/jsx-runtime";
3
- import { useEffect, useRef } from "react";
4
- function SparklineChart({ id, data }) {
5
- const containerRef = useRef(null);
6
- const chartRef = useRef(null);
7
- useEffect(() => {
8
- let cancelled = false;
9
- const el = containerRef.current;
10
- if (!el) return;
11
- const initChart = async () => {
12
- const ApexCharts = (await import("apexcharts")).default;
13
- if (cancelled || !containerRef.current) return;
14
- const options = {
15
- series: [{ data }],
16
- chart: {
17
- type: "area",
18
- height: 50,
19
- sparkline: { enabled: true }
20
- },
21
- stroke: { curve: "straight" },
22
- fill: { opacity: 0.3 },
23
- yaxis: { min: 0 },
24
- colors: ["#DCE6EC"]
25
- };
26
- const chart = new ApexCharts(el, options);
27
- chartRef.current = chart;
28
- chart.render();
29
- };
30
- initChart().catch((err) => console.error("SparklineChart init error:", err));
31
- return () => {
32
- cancelled = true;
33
- if (chartRef.current) {
34
- chartRef.current.destroy();
35
- chartRef.current = null;
3
+ import { useMemo } from "react";
4
+ import { Chart } from "./chart.js";
5
+ import { withAlpha } from "../lib/chart-theme.js";
6
+ function SparklineChart({
7
+ id,
8
+ data,
9
+ color = "#DCE6EC",
10
+ type = "line",
11
+ height = 50,
12
+ ariaLabel = "Sparkline"
13
+ }) {
14
+ const chartData = useMemo(
15
+ () => ({
16
+ labels: data.map((_, i) => String(i + 1)),
17
+ datasets: [
18
+ type === "bar" ? { data, backgroundColor: color, borderRadius: 2, maxBarThickness: 8 } : {
19
+ data,
20
+ borderColor: color,
21
+ backgroundColor: withAlpha(color, 0.3),
22
+ borderWidth: 2,
23
+ fill: "origin",
24
+ tension: 0,
25
+ pointRadius: 0,
26
+ pointHoverRadius: 0
27
+ }
28
+ ]
29
+ }),
30
+ [data, color, type]
31
+ );
32
+ return /* @__PURE__ */ jsx(
33
+ Chart,
34
+ {
35
+ id,
36
+ type,
37
+ height,
38
+ ariaLabel,
39
+ data: chartData,
40
+ options: {
41
+ events: [],
42
+ layout: { padding: 1 },
43
+ plugins: { legend: { display: false }, tooltip: { enabled: false } },
44
+ scales: {
45
+ x: { display: false },
46
+ y: { display: false, min: 0 }
47
+ }
36
48
  }
37
- };
38
- }, [id, data]);
39
- return /* @__PURE__ */ jsx("div", { id, ref: containerRef });
49
+ }
50
+ );
40
51
  }
41
52
  export {
42
53
  SparklineChart
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adminlte/react",
3
- "version": "0.6.1",
3
+ "version": "0.7.0",
4
4
  "description": "AdminLTE 4 React/Next.js component library",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -36,10 +36,11 @@
36
36
  "react",
37
37
  "next",
38
38
  "bootstrap",
39
- "components"
39
+ "components",
40
+ "chartjs"
40
41
  ],
41
42
  "peerDependencies": {
42
- "apexcharts": ">=4.0.0",
43
+ "chart.js": ">=4.5.0",
43
44
  "flatpickr": ">=4.6.0",
44
45
  "jsvectormap": ">=1.6.0",
45
46
  "next": ">=14.0.0",
@@ -51,7 +52,7 @@
51
52
  "tom-select": ">=2.3.0"
52
53
  },
53
54
  "peerDependenciesMeta": {
54
- "apexcharts": {
55
+ "chart.js": {
55
56
  "optional": true
56
57
  },
57
58
  "flatpickr": {
@@ -84,7 +85,7 @@
84
85
  "@types/react-dom": "^19.2.4",
85
86
  "@types/sortablejs": "^1.15.9",
86
87
  "admin-lte": "^4.8.1",
87
- "apexcharts": "^6.10.0",
88
+ "chart.js": "^4.5.1",
88
89
  "eslint": "^10.8.1",
89
90
  "eslint-plugin-react-hooks": "^7.1.1",
90
91
  "flatpickr": "^4.6.13",
@@ -1,40 +0,0 @@
1
- "use client";
2
- import { jsx } from "react/jsx-runtime";
3
- import { useEffect, useRef } from "react";
4
- function ApexChart({ id, series, config }) {
5
- const containerRef = useRef(null);
6
- const chartRef = useRef(null);
7
- const seriesKey = JSON.stringify(series);
8
- const configKey = JSON.stringify(config);
9
- useEffect(() => {
10
- let cancelled = false;
11
- const el = containerRef.current;
12
- if (!el) return;
13
- if (chartRef.current) {
14
- chartRef.current.updateOptions({ ...config, series }, true, true);
15
- return;
16
- }
17
- const initChart = async () => {
18
- const ApexCharts = (await import("apexcharts")).default;
19
- if (cancelled || !containerRef.current) return;
20
- const chart = new ApexCharts(el, { ...config, series });
21
- chartRef.current = chart;
22
- chart.render();
23
- };
24
- initChart().catch((err) => console.error("ApexChart init error:", err));
25
- return () => {
26
- cancelled = true;
27
- };
28
- }, [id, seriesKey, configKey]);
29
- useEffect(
30
- () => () => {
31
- chartRef.current?.destroy();
32
- chartRef.current = null;
33
- },
34
- []
35
- );
36
- return /* @__PURE__ */ jsx("div", { id, ref: containerRef });
37
- }
38
- export {
39
- ApexChart
40
- };