@adminlte/react 0.6.0 → 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 +76 -0
- package/README.md +6 -4
- package/dist/index.d.ts +84 -9
- package/dist/index.js +7 -3
- package/dist/lib/chart-theme.js +120 -0
- package/dist/widget/chart.js +125 -0
- package/dist/widget/sparkline-chart.js +47 -36
- package/package.json +6 -5
- package/dist/widget/apex-chart.js +0 -40
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,79 @@ 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
|
+
|
|
68
|
+
## [0.6.1] - 2026-08-19
|
|
69
|
+
|
|
70
|
+
### Fixed
|
|
71
|
+
|
|
72
|
+
- **The RTL layout page rendered blank under the subpath deploy.** `RtlStyles` hardcoded `href="/css/adminlte.rtl.css"`, which resolves against the *domain root* — so on [adminlte.io/themes/next-react](https://adminlte.io/themes/next-react/) (the `EXPORT=true` build, base path `/themes/next-react`) the request 404'd and the page ran `dir="rtl"` against LTR-compiled AdminLTE CSS: the shell was pushed off-canvas, leaving an empty viewport behind ~10,000 px of horizontal overflow (`document.scrollWidth` **11440** in a 1440 px window). The href now goes through the demo's existing `withBase()` helper — the same one `SubpathLinks` and the page data already use — so it stays `/css/adminlte.rtl.css` at the domain root (local `next dev`) and becomes `/themes/next-react/css/adminlte.rtl.css` in the export. The stylesheet itself was always deployed correctly; only the link was wrong. Verified in **both** modes against real builds: no 404, 1855 rules parsed, `dir="rtl"` applied, `scrollWidth === clientWidth === 1440`.
|
|
73
|
+
- **Mailbox inbox rows overflowed their card.** The row link (`a.flex-grow-1.d-flex.flex-column.flex-md-row`) is itself a flex item, so its `min-width` computed to `auto` and it refused to shrink below its content — the `span.text-truncate` inside it never truncated and the subject text ran past the card edge instead. Both the row link and its elastic middle span now carry `min-width: 0`, matching the core HTML demo (`src/html/pages/mailbox/inbox.astro`). Measured on `/mailbox/inbox`: `.list-group` **1168 / 857** (311 px of clipped text spilling out of the card) → **857 / 857** across all eight rows, with the truncation ellipsis doing its job.
|
|
74
|
+
|
|
75
|
+
### Notes
|
|
76
|
+
|
|
77
|
+
- **Demo-only release.** `src/` is untouched and the published `dist/` is byte-identical to 0.6.0 — nothing an npm consumer imports changed, which is why this is a patch and not 0.7.0. It gets a version at all because the demo is a shipped artifact of this repo and its RTL page was unusable; the fix reaches people when the demo is **redeployed**, not when the package is published, so publishing 0.6.1 to npm is optional.
|
|
78
|
+
- **The demo's CDN pins were re-checked against `package.json`: no mismatch remains.** apexcharts 6.10.0, tabulator-tables 6.5.2, tom-select 2.6.2, quill 2.0.3, flatpickr 4.6.13 and jsvectormap 1.7.0 all match their installed versions; Bootstrap 5.3.8 matches `admin-lte`'s peer range; Bootstrap Icons 1.13.1, OverlayScrollbars 2.16.0, FullCalendar 7.0.2 and Source Sans 3 5.3.0 have no `package.json` counterpart (CDN-only) and are each at their latest release. The `apexcharts@6.8.0` CSS still visible on the live demo is pre-0.6.0 output — that pin was corrected in 0.6.0 and simply has not been redeployed yet.
|
|
79
|
+
- Verified: library build, type-check, lint (0 errors, 13 pre-existing warnings) and 19 unit tests; demo type-check and the `EXPORT=true` subpath export (66 static pages); all 67 Playwright route-smoke + a11y tests; plus a Playwright pass over the built export served at the real `/themes/next-react/` subpath, covering both fixes above.
|
|
80
|
+
|
|
8
81
|
## [0.6.0] - 2026-08-19
|
|
9
82
|
|
|
10
83
|
### Changed
|
|
@@ -227,6 +300,9 @@ Next.js App Router and React Server Components.
|
|
|
227
300
|
- Heavy third-party libraries are never bundled eagerly — they load via dynamic
|
|
228
301
|
`import()` inside the components that use them.
|
|
229
302
|
|
|
303
|
+
[0.7.0]: https://github.com/ColorlibHQ/adminlte-react/compare/v0.6.1...v0.7.0
|
|
304
|
+
[0.6.1]: https://github.com/ColorlibHQ/adminlte-react/compare/v0.6.0...v0.6.1
|
|
305
|
+
[0.6.0]: https://github.com/ColorlibHQ/adminlte-react/compare/v0.5.0...v0.6.0
|
|
230
306
|
[0.5.0]: https://github.com/ColorlibHQ/adminlte-react/compare/v0.4.0...v0.5.0
|
|
231
307
|
[0.4.0]: https://github.com/ColorlibHQ/adminlte-react/compare/v0.3.0...v0.4.0
|
|
232
308
|
[0.3.0]: https://github.com/ColorlibHQ/adminlte-react/compare/v0.2.0...v0.3.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.
|
|
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** -
|
|
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
|
|
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
|
-
- **`
|
|
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 {
|
|
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
|
|
461
|
-
id
|
|
462
|
-
|
|
463
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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 {
|
|
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 {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
-
};
|