@adminlte/react 0.1.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 +161 -0
- package/README.md +454 -0
- package/dist/context/accessibility.js +141 -0
- package/dist/context/body-class-sync.js +22 -0
- package/dist/context/color-mode-context.js +61 -0
- package/dist/context/command-palette-context.js +28 -0
- package/dist/context/html-dir.js +17 -0
- package/dist/context/link-context.js +18 -0
- package/dist/context/lte-behaviors.js +52 -0
- package/dist/context/sidebar-context.js +87 -0
- package/dist/css/adminlte.css +17259 -0
- package/dist/form/button.js +21 -0
- package/dist/form/input-color.js +30 -0
- package/dist/form/input-file.js +29 -0
- package/dist/form/input-flatpickr.js +45 -0
- package/dist/form/input-switch.js +26 -0
- package/dist/form/input-tom-select.js +59 -0
- package/dist/form/input.js +23 -0
- package/dist/form/select.js +25 -0
- package/dist/form/textarea.js +22 -0
- package/dist/hooks/use-card-widget.js +44 -0
- package/dist/hooks/use-direct-chat.js +16 -0
- package/dist/hooks/use-fullscreen.js +44 -0
- package/dist/hooks/use-push-menu.js +54 -0
- package/dist/hooks/use-sortable.js +26 -0
- package/dist/hooks/use-treeview-animation.js +27 -0
- package/dist/index.d.ts +1010 -0
- package/dist/index.js +145 -0
- package/dist/layout/app-content.js +18 -0
- package/dist/layout/auth-layout.js +25 -0
- package/dist/layout/color-mode-toggle.js +67 -0
- package/dist/layout/dashboard-layout.js +101 -0
- package/dist/layout/footer.js +19 -0
- package/dist/layout/sidebar-brand.js +16 -0
- package/dist/layout/sidebar-nav-item.js +46 -0
- package/dist/layout/sidebar-nav.js +43 -0
- package/dist/layout/sidebar-overlay.js +18 -0
- package/dist/layout/sidebar.js +48 -0
- package/dist/layout/topbar.js +128 -0
- package/dist/lib/class-name.js +6 -0
- package/dist/lib/flatten-menu.js +23 -0
- package/dist/tool/datatable.js +45 -0
- package/dist/tool/editor.js +72 -0
- package/dist/tool/modal.js +28 -0
- package/dist/types/direct-chat.js +0 -0
- package/dist/types/layout.js +0 -0
- package/dist/types/menu.js +0 -0
- package/dist/types/theme.js +0 -0
- package/dist/widget/accordion.js +35 -0
- package/dist/widget/alert.js +18 -0
- package/dist/widget/apex-chart.js +40 -0
- package/dist/widget/avatar.js +72 -0
- package/dist/widget/badge.js +20 -0
- package/dist/widget/breadcrumb.js +26 -0
- package/dist/widget/callout.js +11 -0
- package/dist/widget/card.js +84 -0
- package/dist/widget/carousel.js +56 -0
- package/dist/widget/command-palette.js +137 -0
- package/dist/widget/description-block.js +19 -0
- package/dist/widget/direct-chat.js +96 -0
- package/dist/widget/dropdown.js +63 -0
- package/dist/widget/info-box.js +24 -0
- package/dist/widget/list-group.js +47 -0
- package/dist/widget/nav-messages.js +48 -0
- package/dist/widget/nav-notifications.js +40 -0
- package/dist/widget/nav-tasks.js +60 -0
- package/dist/widget/offcanvas.js +61 -0
- package/dist/widget/pagination.js +93 -0
- package/dist/widget/profile-card.js +32 -0
- package/dist/widget/progress-group.js +23 -0
- package/dist/widget/progress.js +25 -0
- package/dist/widget/ratings.js +20 -0
- package/dist/widget/small-box.js +34 -0
- package/dist/widget/sparkline-chart.js +43 -0
- package/dist/widget/spinner.js +26 -0
- package/dist/widget/stepper.js +57 -0
- package/dist/widget/table.js +50 -0
- package/dist/widget/tabs.js +46 -0
- package/dist/widget/timeline.js +15 -0
- package/dist/widget/toast.js +41 -0
- package/dist/widget/tooltip.js +30 -0
- package/dist/widget/world-map.js +42 -0
- package/package.json +116 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useEffect } from "react";
|
|
3
|
+
function Accessibility() {
|
|
4
|
+
useEffect(() => {
|
|
5
|
+
if (typeof document === "undefined") return;
|
|
6
|
+
const created = [];
|
|
7
|
+
const cleanups = [];
|
|
8
|
+
let liveRegion = document.getElementById("live-region");
|
|
9
|
+
if (!liveRegion) {
|
|
10
|
+
liveRegion = document.createElement("div");
|
|
11
|
+
liveRegion.id = "live-region";
|
|
12
|
+
liveRegion.className = "live-region";
|
|
13
|
+
liveRegion.setAttribute("aria-live", "polite");
|
|
14
|
+
liveRegion.setAttribute("aria-atomic", "true");
|
|
15
|
+
liveRegion.setAttribute("role", "status");
|
|
16
|
+
document.body.append(liveRegion);
|
|
17
|
+
created.push(liveRegion);
|
|
18
|
+
}
|
|
19
|
+
const announce = (message, priority = "polite") => {
|
|
20
|
+
if (!liveRegion) return;
|
|
21
|
+
liveRegion.setAttribute("aria-live", priority);
|
|
22
|
+
liveRegion.textContent = message;
|
|
23
|
+
window.setTimeout(() => {
|
|
24
|
+
if (liveRegion) liveRegion.textContent = "";
|
|
25
|
+
}, 1e3);
|
|
26
|
+
};
|
|
27
|
+
if (!document.querySelector(".skip-links")) {
|
|
28
|
+
const container = document.createElement("div");
|
|
29
|
+
container.className = "skip-links";
|
|
30
|
+
const mk = (href, text) => {
|
|
31
|
+
const a = document.createElement("a");
|
|
32
|
+
a.href = href;
|
|
33
|
+
a.className = "skip-link";
|
|
34
|
+
a.textContent = text;
|
|
35
|
+
return a;
|
|
36
|
+
};
|
|
37
|
+
container.append(mk("#main", "Skip to main content"));
|
|
38
|
+
container.append(mk("#navigation", "Skip to navigation"));
|
|
39
|
+
document.body.insertBefore(container, document.body.firstChild);
|
|
40
|
+
created.push(container);
|
|
41
|
+
const main = document.querySelector('#main, main, [role="main"]');
|
|
42
|
+
if (main) {
|
|
43
|
+
if (!main.id) main.id = "main";
|
|
44
|
+
if (!main.hasAttribute("tabindex")) main.setAttribute("tabindex", "-1");
|
|
45
|
+
}
|
|
46
|
+
const nav = document.querySelector('#navigation, nav, [role="navigation"]');
|
|
47
|
+
if (nav) {
|
|
48
|
+
if (!nav.id) nav.id = "navigation";
|
|
49
|
+
if (!nav.hasAttribute("tabindex")) nav.setAttribute("tabindex", "-1");
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
const onKeyDown = (event) => {
|
|
53
|
+
if (event.key !== "Escape") return;
|
|
54
|
+
if (document.querySelector(".modal.show")) return;
|
|
55
|
+
if (document.querySelector(".dropdown-menu.show")) {
|
|
56
|
+
const toggleButton = document.querySelector(
|
|
57
|
+
'[data-bs-toggle="dropdown"][aria-expanded="true"]'
|
|
58
|
+
);
|
|
59
|
+
toggleButton?.click();
|
|
60
|
+
event.preventDefault();
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
document.addEventListener("keydown", onKeyDown);
|
|
64
|
+
cleanups.push(() => document.removeEventListener("keydown", onKeyDown));
|
|
65
|
+
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
|
|
66
|
+
document.body.classList.add("reduce-motion");
|
|
67
|
+
document.documentElement.style.scrollBehavior = "auto";
|
|
68
|
+
const style = document.createElement("style");
|
|
69
|
+
style.textContent = "*, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; }";
|
|
70
|
+
document.head.append(style);
|
|
71
|
+
created.push(style);
|
|
72
|
+
cleanups.push(() => document.body.classList.remove("reduce-motion"));
|
|
73
|
+
}
|
|
74
|
+
const appMain = document.querySelector(".app-main");
|
|
75
|
+
if (appMain) {
|
|
76
|
+
if (!appMain.getAttribute("role")) appMain.setAttribute("role", "main");
|
|
77
|
+
if (!appMain.id) appMain.id = "main";
|
|
78
|
+
}
|
|
79
|
+
document.querySelectorAll(".navbar-nav, .nav").forEach((nav, index) => {
|
|
80
|
+
if (nav.tagName === "UL" || nav.tagName === "OL") return;
|
|
81
|
+
if (!nav.hasAttribute("role")) nav.setAttribute("role", "navigation");
|
|
82
|
+
if (!nav.hasAttribute("aria-label")) nav.setAttribute("aria-label", `Navigation ${index + 1}`);
|
|
83
|
+
});
|
|
84
|
+
document.querySelectorAll("table").forEach((table) => {
|
|
85
|
+
if (!table.hasAttribute("role")) table.setAttribute("role", "table");
|
|
86
|
+
table.querySelectorAll("th").forEach((th) => {
|
|
87
|
+
if (!th.hasAttribute("scope")) {
|
|
88
|
+
if (th.closest("thead")) th.setAttribute("scope", "col");
|
|
89
|
+
else if (th.cellIndex === 0) th.setAttribute("scope", "row");
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
document.querySelectorAll("input, select, textarea").forEach((input) => {
|
|
94
|
+
if (!input.labels?.length && !input.hasAttribute("aria-label") && !input.hasAttribute("aria-labelledby")) {
|
|
95
|
+
const placeholder = input.getAttribute("placeholder");
|
|
96
|
+
if (placeholder) input.setAttribute("aria-label", placeholder);
|
|
97
|
+
}
|
|
98
|
+
if (!input.classList.contains("disable-adminlte-validations")) {
|
|
99
|
+
const onInvalid = () => {
|
|
100
|
+
input.classList.add("is-invalid");
|
|
101
|
+
announce(
|
|
102
|
+
`Error in ${input.labels?.[0]?.textContent || input.name}: ${input.validationMessage}`,
|
|
103
|
+
"assertive"
|
|
104
|
+
);
|
|
105
|
+
};
|
|
106
|
+
input.addEventListener("invalid", onInvalid);
|
|
107
|
+
cleanups.push(() => input.removeEventListener("invalid", onInvalid));
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
const focusHistory = [];
|
|
111
|
+
const onModalShown = (e) => {
|
|
112
|
+
const modal = e.target;
|
|
113
|
+
const focusable = modal.querySelector(
|
|
114
|
+
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
|
|
115
|
+
);
|
|
116
|
+
focusHistory.push(document.activeElement);
|
|
117
|
+
focusable?.focus();
|
|
118
|
+
};
|
|
119
|
+
const onModalHidden = () => focusHistory.pop()?.focus();
|
|
120
|
+
const onDropdownShown = (e) => {
|
|
121
|
+
const dropdown = e.target;
|
|
122
|
+
dropdown.querySelector(".dropdown-menu a, .dropdown-menu button")?.focus();
|
|
123
|
+
};
|
|
124
|
+
document.addEventListener("shown.bs.modal", onModalShown);
|
|
125
|
+
document.addEventListener("hidden.bs.modal", onModalHidden);
|
|
126
|
+
document.addEventListener("shown.bs.dropdown", onDropdownShown);
|
|
127
|
+
cleanups.push(() => {
|
|
128
|
+
document.removeEventListener("shown.bs.modal", onModalShown);
|
|
129
|
+
document.removeEventListener("hidden.bs.modal", onModalHidden);
|
|
130
|
+
document.removeEventListener("shown.bs.dropdown", onDropdownShown);
|
|
131
|
+
});
|
|
132
|
+
return () => {
|
|
133
|
+
cleanups.forEach((fn) => fn());
|
|
134
|
+
created.forEach((el) => el.remove());
|
|
135
|
+
};
|
|
136
|
+
}, []);
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
export {
|
|
140
|
+
Accessibility
|
|
141
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useEffect } from "react";
|
|
3
|
+
import { useSidebarContext } from "./sidebar-context.js";
|
|
4
|
+
function BodyClassSync({ staticClasses }) {
|
|
5
|
+
const { isCollapsed, isMobileOpen, isMiniMode } = useSidebarContext();
|
|
6
|
+
useEffect(() => {
|
|
7
|
+
const body = document.body;
|
|
8
|
+
const staticList = staticClasses.split(" ").filter(Boolean);
|
|
9
|
+
staticList.forEach((cls) => body.classList.add(cls));
|
|
10
|
+
body.classList.toggle("sidebar-collapse", isCollapsed);
|
|
11
|
+
body.classList.toggle("sidebar-open", isMobileOpen);
|
|
12
|
+
body.classList.toggle("sidebar-mini", isMiniMode);
|
|
13
|
+
return () => {
|
|
14
|
+
staticList.forEach((cls) => body.classList.remove(cls));
|
|
15
|
+
body.classList.remove("sidebar-collapse", "sidebar-open", "sidebar-mini");
|
|
16
|
+
};
|
|
17
|
+
}, [isCollapsed, isMobileOpen, isMiniMode, staticClasses]);
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
export {
|
|
21
|
+
BodyClassSync
|
|
22
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { createContext, useContext, useEffect, useMemo, useState } from "react";
|
|
4
|
+
const ColorModeContext = createContext(void 0);
|
|
5
|
+
function ColorModeProvider({
|
|
6
|
+
initialMode = "auto",
|
|
7
|
+
children
|
|
8
|
+
}) {
|
|
9
|
+
const [colorMode, setColorMode] = useState(initialMode);
|
|
10
|
+
const [resolvedMode, setResolvedMode] = useState("light");
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
if (typeof window === "undefined") return;
|
|
13
|
+
const saved = localStorage.getItem("lte-theme");
|
|
14
|
+
if (saved && ["light", "dark", "auto"].includes(saved)) {
|
|
15
|
+
setColorMode(saved);
|
|
16
|
+
}
|
|
17
|
+
}, []);
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
if (typeof window === "undefined") return;
|
|
20
|
+
let resolved;
|
|
21
|
+
if (colorMode === "auto") {
|
|
22
|
+
resolved = window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
|
23
|
+
} else {
|
|
24
|
+
resolved = colorMode;
|
|
25
|
+
}
|
|
26
|
+
setResolvedMode(resolved);
|
|
27
|
+
document.documentElement.setAttribute("data-bs-theme", resolved);
|
|
28
|
+
localStorage.setItem("lte-theme", colorMode);
|
|
29
|
+
}, [colorMode]);
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
if (colorMode !== "auto" || typeof window === "undefined") return;
|
|
32
|
+
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
|
33
|
+
const handleChange = () => {
|
|
34
|
+
const resolved = mediaQuery.matches ? "dark" : "light";
|
|
35
|
+
setResolvedMode(resolved);
|
|
36
|
+
document.documentElement.setAttribute("data-bs-theme", resolved);
|
|
37
|
+
};
|
|
38
|
+
mediaQuery.addEventListener("change", handleChange);
|
|
39
|
+
return () => mediaQuery.removeEventListener("change", handleChange);
|
|
40
|
+
}, [colorMode]);
|
|
41
|
+
const value = useMemo(
|
|
42
|
+
() => ({
|
|
43
|
+
colorMode,
|
|
44
|
+
setColorMode,
|
|
45
|
+
resolvedMode
|
|
46
|
+
}),
|
|
47
|
+
[colorMode, resolvedMode]
|
|
48
|
+
);
|
|
49
|
+
return /* @__PURE__ */ jsx(ColorModeContext.Provider, { value, children });
|
|
50
|
+
}
|
|
51
|
+
function useColorModeContext() {
|
|
52
|
+
const context = useContext(ColorModeContext);
|
|
53
|
+
if (!context) {
|
|
54
|
+
throw new Error("useColorModeContext must be used within ColorModeProvider");
|
|
55
|
+
}
|
|
56
|
+
return context;
|
|
57
|
+
}
|
|
58
|
+
export {
|
|
59
|
+
ColorModeProvider,
|
|
60
|
+
useColorModeContext
|
|
61
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { createContext, useContext, useState, useEffect, useCallback } from "react";
|
|
4
|
+
const CommandPaletteContext = createContext(void 0);
|
|
5
|
+
function CommandPaletteProvider({ children }) {
|
|
6
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
7
|
+
const open = useCallback(() => setIsOpen(true), []);
|
|
8
|
+
const close = useCallback(() => setIsOpen(false), []);
|
|
9
|
+
const toggle = useCallback(() => setIsOpen((o) => !o), []);
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
const onKey = (e) => {
|
|
12
|
+
if ((e.metaKey || e.ctrlKey) && (e.key === "k" || e.key === "K")) {
|
|
13
|
+
e.preventDefault();
|
|
14
|
+
toggle();
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
document.addEventListener("keydown", onKey);
|
|
18
|
+
return () => document.removeEventListener("keydown", onKey);
|
|
19
|
+
}, [toggle]);
|
|
20
|
+
return /* @__PURE__ */ jsx(CommandPaletteContext.Provider, { value: { isOpen, open, close, toggle }, children });
|
|
21
|
+
}
|
|
22
|
+
function useCommandPalette() {
|
|
23
|
+
return useContext(CommandPaletteContext);
|
|
24
|
+
}
|
|
25
|
+
export {
|
|
26
|
+
CommandPaletteProvider,
|
|
27
|
+
useCommandPalette
|
|
28
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useEffect } from "react";
|
|
3
|
+
function HtmlDir({ dir }) {
|
|
4
|
+
useEffect(() => {
|
|
5
|
+
const root = document.documentElement;
|
|
6
|
+
const previous = root.getAttribute("dir");
|
|
7
|
+
root.setAttribute("dir", dir);
|
|
8
|
+
return () => {
|
|
9
|
+
if (previous) root.setAttribute("dir", previous);
|
|
10
|
+
else root.removeAttribute("dir");
|
|
11
|
+
};
|
|
12
|
+
}, [dir]);
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
export {
|
|
16
|
+
HtmlDir
|
|
17
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { createContext, useContext } from "react";
|
|
4
|
+
const DefaultLink = ({ href, children, ...rest }) => /* @__PURE__ */ jsx("a", { href, ...rest, children });
|
|
5
|
+
const LinkContext = createContext(DefaultLink);
|
|
6
|
+
function LinkProvider({
|
|
7
|
+
component,
|
|
8
|
+
children
|
|
9
|
+
}) {
|
|
10
|
+
return /* @__PURE__ */ jsx(LinkContext.Provider, { value: component ?? DefaultLink, children });
|
|
11
|
+
}
|
|
12
|
+
function useLinkComponent() {
|
|
13
|
+
return useContext(LinkContext);
|
|
14
|
+
}
|
|
15
|
+
export {
|
|
16
|
+
LinkProvider,
|
|
17
|
+
useLinkComponent
|
|
18
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useEffect } from "react";
|
|
3
|
+
function LteBehaviors() {
|
|
4
|
+
useEffect(() => {
|
|
5
|
+
const onClick = (e) => {
|
|
6
|
+
const target = e.target;
|
|
7
|
+
const btn = target?.closest("[data-lte-toggle]");
|
|
8
|
+
if (!btn) return;
|
|
9
|
+
const action = btn.getAttribute("data-lte-toggle");
|
|
10
|
+
const card = btn.closest(".card");
|
|
11
|
+
switch (action) {
|
|
12
|
+
case "card-collapse":
|
|
13
|
+
card?.classList.toggle("collapsed-card");
|
|
14
|
+
break;
|
|
15
|
+
case "card-maximize":
|
|
16
|
+
card?.classList.toggle("maximized-card");
|
|
17
|
+
break;
|
|
18
|
+
case "card-remove":
|
|
19
|
+
card?.remove();
|
|
20
|
+
break;
|
|
21
|
+
case "chat-pane": {
|
|
22
|
+
const chat = btn.closest(".direct-chat") ?? card;
|
|
23
|
+
chat?.classList.toggle("direct-chat-contacts-open");
|
|
24
|
+
break;
|
|
25
|
+
}
|
|
26
|
+
default:
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
const onSubmit = (e) => {
|
|
31
|
+
const form = e.target?.closest(
|
|
32
|
+
".needs-validation, .needs-validation-tooltip"
|
|
33
|
+
);
|
|
34
|
+
if (!form) return;
|
|
35
|
+
if (!form.checkValidity()) {
|
|
36
|
+
e.preventDefault();
|
|
37
|
+
e.stopPropagation();
|
|
38
|
+
}
|
|
39
|
+
form.classList.add("was-validated");
|
|
40
|
+
};
|
|
41
|
+
document.addEventListener("click", onClick);
|
|
42
|
+
document.addEventListener("submit", onSubmit, true);
|
|
43
|
+
return () => {
|
|
44
|
+
document.removeEventListener("click", onClick);
|
|
45
|
+
document.removeEventListener("submit", onSubmit, true);
|
|
46
|
+
};
|
|
47
|
+
}, []);
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
export {
|
|
51
|
+
LteBehaviors
|
|
52
|
+
};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { createContext, useCallback, useContext, useEffect, useMemo, useState } from "react";
|
|
4
|
+
const SidebarContext = createContext(void 0);
|
|
5
|
+
function SidebarProvider({
|
|
6
|
+
sidebarMini = false,
|
|
7
|
+
enablePersistence = false,
|
|
8
|
+
sidebarBreakpoint = 992,
|
|
9
|
+
children
|
|
10
|
+
}) {
|
|
11
|
+
const [isCollapsed, setIsCollapsed] = useState(false);
|
|
12
|
+
const [isMobileOpen, setIsMobileOpen] = useState(false);
|
|
13
|
+
const [isMiniMode] = useState(sidebarMini);
|
|
14
|
+
const [windowWidth, setWindowWidth] = useState(null);
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
if (typeof window === "undefined") return;
|
|
17
|
+
setWindowWidth(window.innerWidth);
|
|
18
|
+
if (enablePersistence) {
|
|
19
|
+
const saved = localStorage.getItem("lte.sidebar.state");
|
|
20
|
+
if (saved) {
|
|
21
|
+
try {
|
|
22
|
+
const { collapsed } = JSON.parse(saved);
|
|
23
|
+
setIsCollapsed(collapsed);
|
|
24
|
+
} catch {
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}, [enablePersistence]);
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
if (typeof window === "undefined" || !enablePersistence) return;
|
|
31
|
+
localStorage.setItem("lte.sidebar.state", JSON.stringify({ collapsed: isCollapsed }));
|
|
32
|
+
}, [isCollapsed, enablePersistence]);
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
const handleResize = () => {
|
|
35
|
+
setWindowWidth(window.innerWidth);
|
|
36
|
+
};
|
|
37
|
+
window.addEventListener("resize", handleResize);
|
|
38
|
+
return () => window.removeEventListener("resize", handleResize);
|
|
39
|
+
}, []);
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
if (windowWidth === null) return;
|
|
42
|
+
const isMobileSize = windowWidth <= sidebarBreakpoint;
|
|
43
|
+
if (!isMobileSize && isMobileOpen) {
|
|
44
|
+
setIsMobileOpen(false);
|
|
45
|
+
}
|
|
46
|
+
}, [windowWidth, sidebarBreakpoint, isMobileOpen]);
|
|
47
|
+
const toggle = useCallback(() => {
|
|
48
|
+
const w = windowWidth ?? (typeof window !== "undefined" ? window.innerWidth : 9999);
|
|
49
|
+
if (w <= sidebarBreakpoint) {
|
|
50
|
+
setIsMobileOpen((v) => !v);
|
|
51
|
+
} else {
|
|
52
|
+
setIsCollapsed((v) => !v);
|
|
53
|
+
}
|
|
54
|
+
}, [windowWidth, sidebarBreakpoint]);
|
|
55
|
+
const collapse = useCallback(() => {
|
|
56
|
+
setIsCollapsed(true);
|
|
57
|
+
setIsMobileOpen(false);
|
|
58
|
+
}, []);
|
|
59
|
+
const expand = useCallback(() => {
|
|
60
|
+
setIsCollapsed(false);
|
|
61
|
+
setIsMobileOpen(false);
|
|
62
|
+
}, []);
|
|
63
|
+
const value = useMemo(
|
|
64
|
+
() => ({
|
|
65
|
+
isCollapsed,
|
|
66
|
+
isMobileOpen,
|
|
67
|
+
isMiniMode,
|
|
68
|
+
toggle,
|
|
69
|
+
collapse,
|
|
70
|
+
expand,
|
|
71
|
+
sidebarBreakpoint
|
|
72
|
+
}),
|
|
73
|
+
[isCollapsed, isMobileOpen, isMiniMode, toggle, collapse, expand, sidebarBreakpoint]
|
|
74
|
+
);
|
|
75
|
+
return /* @__PURE__ */ jsx(SidebarContext.Provider, { value, children });
|
|
76
|
+
}
|
|
77
|
+
function useSidebarContext() {
|
|
78
|
+
const context = useContext(SidebarContext);
|
|
79
|
+
if (!context) {
|
|
80
|
+
throw new Error("useSidebarContext must be used within SidebarProvider");
|
|
81
|
+
}
|
|
82
|
+
return context;
|
|
83
|
+
}
|
|
84
|
+
export {
|
|
85
|
+
SidebarProvider,
|
|
86
|
+
useSidebarContext
|
|
87
|
+
};
|