framepexls-ui-lib 0.1.8 → 0.1.9

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.
@@ -0,0 +1,407 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var Sidebar_exports = {};
30
+ __export(Sidebar_exports, {
31
+ default: () => Sidebar
32
+ });
33
+ module.exports = __toCommonJS(Sidebar_exports);
34
+ var import_jsx_runtime = require("react/jsx-runtime");
35
+ var import_react = require("react");
36
+ var import_react2 = require("motion/react");
37
+ var import_Button = __toESM(require("./Button"));
38
+ var import_AvatarSquare = __toESM(require("./AvatarSquare"));
39
+ var import_iconos = require("./iconos");
40
+ function groupItems(items) {
41
+ var _a;
42
+ const byGroup = /* @__PURE__ */ new Map();
43
+ for (const it of items) {
44
+ const arr = (_a = byGroup.get(it.group)) != null ? _a : [];
45
+ arr.push(it);
46
+ byGroup.set(it.group, arr);
47
+ }
48
+ return Array.from(byGroup.entries());
49
+ }
50
+ function GroupHeader({ title }) {
51
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "mb-3 px-1 text-[11px] font-semibold uppercase tracking-wider text-slate-400", children: title });
52
+ }
53
+ function ChevronDown({ open }) {
54
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { className: `h-4 w-4 transition-transform ${open ? "rotate-180" : ""}`, viewBox: "0 0 20 20", fill: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
55
+ "path",
56
+ {
57
+ fillRule: "evenodd",
58
+ d: "M5.23 7.21a.75.75 0 011.06.02L10 11.127l3.71-3.896a.75.75 0 111.08 1.04l-4.24 4.46a.75.75 0 01-1.08 0L5.21 8.27a.75.75 0 01.02-1.06z",
59
+ clipRule: "evenodd"
60
+ }
61
+ ) });
62
+ }
63
+ function Sidebar({
64
+ items,
65
+ activeKey,
66
+ onNavigate,
67
+ user,
68
+ userMenuSlot,
69
+ onBrandClick,
70
+ collapsedKey = "ga:sidebar-collapsed",
71
+ brandInitials = "C",
72
+ brandTitle = "Campaign",
73
+ brandSubtitle = "Tablero"
74
+ }) {
75
+ const [drawerOpen, setDrawerOpen] = (0, import_react.useState)(false);
76
+ const [collapsed, setCollapsed] = (0, import_react.useState)(false);
77
+ (0, import_react.useEffect)(() => {
78
+ try {
79
+ if (localStorage.getItem(collapsedKey) === "1") setCollapsed(true);
80
+ } catch {
81
+ }
82
+ }, [collapsedKey]);
83
+ const toggleCollapsed = () => {
84
+ setCollapsed((c) => {
85
+ const n = !c;
86
+ try {
87
+ localStorage.setItem(collapsedKey, n ? "1" : "0");
88
+ } catch {
89
+ }
90
+ return n;
91
+ });
92
+ };
93
+ (0, import_react.useEffect)(() => {
94
+ if (drawerOpen) document.body.style.overflow = "hidden";
95
+ else document.body.style.overflow = "";
96
+ return () => {
97
+ document.body.style.overflow = "";
98
+ };
99
+ }, [drawerOpen]);
100
+ const groups = (0, import_react.useMemo)(() => groupItems(items), [items]);
101
+ const go = (key) => {
102
+ onNavigate(key);
103
+ setDrawerOpen(false);
104
+ };
105
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
106
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "sticky top-0 z-40 flex h-14 items-center gap-2 border-b border-black/5 bg-white/80 px-3 backdrop-blur shadow-sm xl:hidden dark:bg-[#0b0a0a]/70", children: [
107
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
108
+ import_Button.default,
109
+ {
110
+ noPaddingX: true,
111
+ "aria-label": "Abrir men\xFA",
112
+ onClick: () => setDrawerOpen(true),
113
+ variant: "ghost",
114
+ size: "sm",
115
+ leftIcon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_iconos.MenuIcon, {}),
116
+ className: "h-9 w-9 p-0 !gap-0 rounded-xl border border-slate-200 bg-white text-slate-700 shadow-sm hover:bg-slate-50 active:scale-95 dark:border-white/10 dark:bg-white/5 dark:text-slate-200 dark:hover:bg-white/10"
117
+ }
118
+ ),
119
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { onClick: onBrandClick, className: "flex items-center gap-2", children: [
120
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "text-2xl font-black leading-none tracking-tight", children: brandInitials }),
121
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
122
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "text-sm font-semibold leading-4", children: brandTitle }),
123
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "text-xs text-slate-500 text-left", children: brandSubtitle })
124
+ ] })
125
+ ] })
126
+ ] }),
127
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
128
+ "aside",
129
+ {
130
+ className: [
131
+ "hidden xl:fixed xl:inset-y-0 xl:flex xl:flex-col xl:border-r xl:border-black/5 xl:bg-white xl:py-6 dark:xl:bg-[#0b0a0a]/70 z-40",
132
+ "transition-[width] duration-200",
133
+ collapsed ? "xl:w-28 xl:px-3" : "xl:w-72 xl:px-4"
134
+ ].join(" "),
135
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
136
+ SidebarInner,
137
+ {
138
+ groups,
139
+ collapsed,
140
+ toggleCollapsed,
141
+ activeKey,
142
+ go,
143
+ user,
144
+ userMenuSlot,
145
+ brandInitials,
146
+ brandTitle,
147
+ brandSubtitle
148
+ }
149
+ )
150
+ }
151
+ ),
152
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: ["hidden xl:block", "transition-[width] duration-200", collapsed ? "xl:w-20" : "xl:w-72"].join(" ") }),
153
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react2.AnimatePresence, { children: drawerOpen && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react2.motion.div, { className: "fixed inset-0 z-40 xl:hidden", initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, children: [
154
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "absolute inset-0 bg-black/40" }),
155
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
156
+ import_react2.motion.div,
157
+ {
158
+ role: "dialog",
159
+ "aria-modal": "true",
160
+ initial: { x: -340 },
161
+ animate: { x: 0 },
162
+ exit: { x: -340 },
163
+ transition: { type: "spring", stiffness: 260, damping: 28 },
164
+ className: "relative h-full w-[88%] max-w-80 bg-white px-5 py-6 shadow-2xl dark:bg-[#0b0a0a]",
165
+ children: [
166
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "mb-4 flex items-center justify-between", children: [
167
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
168
+ import_Button.default,
169
+ {
170
+ noPaddingX: true,
171
+ onClick: () => setDrawerOpen(false),
172
+ variant: "ghost",
173
+ size: "sm",
174
+ leftIcon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_iconos.CloseIcon, {}),
175
+ className: "h-9 w-9 p-0 !gap-0 rounded-xl border border-slate-200 bg-white text-slate-700 shadow-sm hover:bg-slate-50 dark:border-white/10 dark:bg-white/5 dark:text-slate-200"
176
+ }
177
+ ),
178
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "text-sm text-slate-500", children: "Men\xFA" })
179
+ ] }),
180
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
181
+ SidebarInner,
182
+ {
183
+ groups,
184
+ mobile: true,
185
+ activeKey,
186
+ go,
187
+ user,
188
+ userMenuSlot,
189
+ brandInitials,
190
+ brandTitle,
191
+ brandSubtitle
192
+ }
193
+ )
194
+ ]
195
+ }
196
+ )
197
+ ] }) })
198
+ ] });
199
+ }
200
+ function SidebarInner({
201
+ groups,
202
+ collapsed,
203
+ toggleCollapsed,
204
+ mobile,
205
+ activeKey,
206
+ go,
207
+ user,
208
+ userMenuSlot,
209
+ brandInitials,
210
+ brandTitle,
211
+ brandSubtitle
212
+ }) {
213
+ var _a, _b, _c, _d;
214
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex h-full flex-col", children: [
215
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center justify-between px-1", children: [
216
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { className: "flex items-center gap-3", title: "Ir al inicio", children: [
217
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "text-2xl font-black tracking-tight", children: brandInitials }),
218
+ !collapsed && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "leading-tight", children: [
219
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "text-lg font-semibold tracking-tight", children: brandTitle }),
220
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "text-[11px] uppercase tracking-wide text-slate-400 text-left", children: brandSubtitle })
221
+ ] })
222
+ ] }),
223
+ !mobile && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
224
+ import_Button.default,
225
+ {
226
+ noPaddingX: true,
227
+ "aria-label": collapsed ? "Expandir barra lateral" : "Contraer barra lateral",
228
+ onClick: toggleCollapsed,
229
+ variant: "ghost",
230
+ size: "sm",
231
+ leftIcon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_iconos.ChevronLeftRightIcon, { collapsed: collapsed != null ? collapsed : false }),
232
+ className: "hidden xl:inline-flex h-8 w-8 p-0 !gap-0 rounded-xl border border-slate-200 bg-white text-slate-700 shadow-sm hover:bg-slate-50 dark:border-white/10 dark:bg-white/5 dark:text-slate-200",
233
+ title: collapsed ? "Expandir" : "Contraer"
234
+ }
235
+ )
236
+ ] }),
237
+ collapsed && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "mt-4 flex items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_AvatarSquare.default, { size: 48, src: (_a = user == null ? void 0 : user.avatarUrl) != null ? _a : void 0, radiusClass: "rounded-2xl" }) }),
238
+ !collapsed && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "mt-4 flex items-center gap-3 rounded-2xl border border-slate-200 bg-white p-2 pr-3 shadow-sm dark:border-white/10 dark:bg:white/5 dark:bg-white/5", children: [
239
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_AvatarSquare.default, { size: 48, src: (_b = user == null ? void 0 : user.avatarUrl) != null ? _b : void 0, radiusClass: "rounded-2xl" }),
240
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "min-w-0", children: [
241
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "truncate text-sm font-semibold", children: (_c = user == null ? void 0 : user.name) != null ? _c : "Usuario" }),
242
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "text-xs text-slate-500", children: (_d = user == null ? void 0 : user.rol_principal) != null ? _d : "\u2014" })
243
+ ] }),
244
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "ml-auto", children: userMenuSlot })
245
+ ] }),
246
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("nav", { className: "my-6 flex-1 space-y-7 overflow-auto overflow-x-hidden px-2", children: groups.map(([groupName, items]) => {
247
+ const visibles = items.filter((i) => {
248
+ var _a2;
249
+ return ((_a2 = i.children) == null ? void 0 : _a2.length) ? i.children.some((c) => c) : true;
250
+ });
251
+ if (visibles.length === 0) return null;
252
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
253
+ !collapsed && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GroupHeader, { title: groupName }),
254
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "space-y-2", children: visibles.map((item) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RenderItem, { item, collapsed: collapsed != null ? collapsed : false, activeKey, go }, item.key)) })
255
+ ] }, groupName);
256
+ }) })
257
+ ] });
258
+ }
259
+ function RenderItem({
260
+ item,
261
+ collapsed,
262
+ activeKey,
263
+ go
264
+ }) {
265
+ var _a, _b, _c;
266
+ const Icon = (_a = item.icon) != null ? _a : (() => null);
267
+ const hasChildren = !!((_b = item.children) == null ? void 0 : _b.length);
268
+ const children = ((_c = item.children) != null ? _c : []).filter(Boolean);
269
+ const isActive = item.key === activeKey || hasChildren && children.some((ch) => ch.key === activeKey);
270
+ const [open, setOpen] = (0, import_react.useState)(false);
271
+ (0, import_react.useEffect)(() => {
272
+ if (!hasChildren) return;
273
+ try {
274
+ const raw = localStorage.getItem(`ga:submenu:${item.key}`);
275
+ if (raw === "1") setOpen(true);
276
+ } catch {
277
+ }
278
+ }, [hasChildren, item.key]);
279
+ (0, import_react.useEffect)(() => {
280
+ if (!hasChildren) return;
281
+ try {
282
+ localStorage.setItem(`ga:submenu:${item.key}`, open ? "1" : "0");
283
+ } catch {
284
+ }
285
+ }, [hasChildren, open, item.key]);
286
+ if (hasChildren && collapsed) {
287
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
288
+ "button",
289
+ {
290
+ onClick: () => go(item.key),
291
+ title: item.label,
292
+ className: [
293
+ "group flex w-full items-center gap-3 rounded-2xl px-3 py-2 text-left text-[15px] transition justify-center",
294
+ isActive ? "bg-blue-600/20 text-slate-900 ring-1 ring-blue-300 dark:bg-blue-600/25 dark:text-white" : "text-slate-700 hover:bg-slate-100 dark:text-slate-200 dark:hover:bg-white/5"
295
+ ].join(" "),
296
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
297
+ "span",
298
+ {
299
+ className: [
300
+ "grid h-9 w-9 place-items-center rounded-xl border transition",
301
+ isActive ? "border-blue-300/60 bg-white shadow-sm dark:border-blue-700/40 dark:bg-white/10" : "border-slate-200 bg-white shadow-sm dark:border-white/10 dark:bg-white/5"
302
+ ].join(" "),
303
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, { className: "h-5 w-5" })
304
+ }
305
+ )
306
+ }
307
+ );
308
+ }
309
+ if (hasChildren) {
310
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
311
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
312
+ "div",
313
+ {
314
+ className: [
315
+ "flex w-full min-w-0 items-center rounded-2xl px-3 py-2 text-[15px] transition gap-2",
316
+ isActive ? "bg-blue-600/20 text-slate-900 ring-1 ring-blue-300 dark:bg-blue-600/25 dark:text-white" : "text-slate-700 hover:bg-slate-100 dark:text-slate-200 dark:hover:bg-white/5"
317
+ ].join(" "),
318
+ children: [
319
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", onClick: () => go(item.key), className: "flex min-w-0 flex-1 items-center gap-3", children: [
320
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
321
+ "span",
322
+ {
323
+ className: [
324
+ "grid h-9 w-9 place-items-center rounded-xl border transition",
325
+ isActive ? "border-blue-300/60 bg-white shadow-sm dark:border-blue-700/40 dark:bg-white/10" : "border-slate-200 bg-white shadow-sm dark:border-white/10 dark:bg-white/5"
326
+ ].join(" "),
327
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, { className: "h-5 w-5" })
328
+ }
329
+ ),
330
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "truncate", children: item.label })
331
+ ] }),
332
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
333
+ import_Button.default,
334
+ {
335
+ type: "button",
336
+ "aria-label": open ? "Contraer submen\xFA" : "Expandir submen\xFA",
337
+ onClick: (e) => {
338
+ e.stopPropagation();
339
+ setOpen(!open);
340
+ },
341
+ variant: "ghost",
342
+ size: "sm",
343
+ leftIcon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronDown, { open }),
344
+ className: [
345
+ "h-8 w-8 shrink-0 rounded-lg p-0 !gap-0 border",
346
+ isActive ? "border-blue-200 bg-white/80 dark:border-blue-700/40 dark:bg-white/10" : "border-slate-200 bg-white dark:border-white/10 dark:bg-white/5"
347
+ ].join(" ")
348
+ }
349
+ )
350
+ ]
351
+ }
352
+ ),
353
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react2.AnimatePresence, { initial: false, children: open && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
354
+ import_react2.motion.div,
355
+ {
356
+ initial: { height: 0, opacity: 0, y: -4 },
357
+ animate: { height: "auto", opacity: 1, y: 0 },
358
+ exit: { height: 0, opacity: 0, y: -4 },
359
+ transition: { type: "tween", duration: 0.18 },
360
+ className: "mt-1 overflow-hidden",
361
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rounded-xl border border-slate-200 bg-white shadow-sm dark:border-white/10 dark:bg-white/5", children: children.map((ch) => {
362
+ const active2 = ch.key === activeKey;
363
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
364
+ "button",
365
+ {
366
+ onClick: () => go(ch.key),
367
+ className: [
368
+ "block w-full text-left px-3 py-2 text-[14px] transition",
369
+ active2 ? "bg-blue-50 text-blue-700 ring-1 ring-blue-200 dark:bg-blue-900/20 dark:text-blue-200 dark:ring-blue-700/40 rounded-xl" : "text-slate-700 hover:bg-slate-50 dark:text-slate-200 dark:hover:bg-white/10"
370
+ ].join(" "),
371
+ children: ch.label
372
+ },
373
+ ch.key
374
+ );
375
+ }) })
376
+ },
377
+ `submenu-${item.key}`
378
+ ) })
379
+ ] });
380
+ }
381
+ const active = item.key === activeKey;
382
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
383
+ "button",
384
+ {
385
+ onClick: () => go(item.key),
386
+ title: collapsed ? item.label : void 0,
387
+ className: [
388
+ "group flex w-full min-w-0 items-center gap-3 rounded-2xl px-3 py-2 text-left text-[15px] transition",
389
+ active ? "bg-blue-600/20 text-slate-900 ring-1 ring-blue-300 dark:bg-blue-600/25 dark:text-white" : "text-slate-700 hover:bg-slate-100 dark:text-slate-200 dark:hover:bg-white/5",
390
+ collapsed ? "justify-center" : ""
391
+ ].join(" "),
392
+ children: [
393
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
394
+ "span",
395
+ {
396
+ className: [
397
+ "grid h-9 w-9 place-items-center rounded-xl border text-slate-700 transition group-hover:scale-105 dark:text-slate-200",
398
+ active ? "border-blue-300/60 bg-white shadow-sm dark:border-blue-700/40 dark:bg-white/10" : "border-slate-200 bg-white shadow-sm dark:border-white/10 dark:bg-white/5"
399
+ ].join(" "),
400
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, { className: "h-5 w-5" })
401
+ }
402
+ ),
403
+ !collapsed && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "truncate", children: item.label })
404
+ ]
405
+ }
406
+ );
407
+ }