@nubitio/admin 0.5.29 → 0.5.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -21,15 +21,90 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
21
21
  enumerable: true
22
22
  }) : target, mod));
23
23
  //#endregion
24
+ let react_router_dom = require("react-router-dom");
25
+ let react_jsx_runtime = require("react/jsx-runtime");
24
26
  let react = require("react");
25
27
  react = __toESM(react, 1);
26
- let react_router_dom = require("react-router-dom");
27
28
  let _nubitio_ui = require("@nubitio/ui");
28
- let react_jsx_runtime = require("react/jsx-runtime");
29
29
  let _tanstack_react_query = require("@tanstack/react-query");
30
30
  let _nubitio_core = require("@nubitio/core");
31
31
  let _nubitio_crud = require("@nubitio/crud");
32
32
  let _nubitio_hydra = require("@nubitio/hydra");
33
+ //#region packages/admin/FeatureHubLayout.tsx
34
+ function normalizeIcon(icon) {
35
+ if (!icon) return void 0;
36
+ if (icon.startsWith("ph ")) return icon;
37
+ if (icon.startsWith("ph-")) return `ph ${icon}`;
38
+ return `ph ph-${icon}`;
39
+ }
40
+ function normalizeBasePath(path) {
41
+ return path.endsWith("/") && path.length > 1 ? path.slice(0, -1) : path;
42
+ }
43
+ function FeatureHubLayout({ title, subtitle, tabs, basePath, defaultPath, banner, tabsAriaLabel = "Secciones", density = "default", className = "" }) {
44
+ const location = (0, react_router_dom.useLocation)();
45
+ const normalizedBase = normalizeBasePath(basePath);
46
+ const visibleTabs = tabs.filter((tab) => tab.visible !== false);
47
+ const compact = density === "compact";
48
+ const densityClass = compact ? " nb-feature-hub--compact" : "";
49
+ const showBanner = banner && banner.visible !== false && (!compact || banner.showInCompact === true);
50
+ if (location.pathname === normalizedBase || location.pathname === `${normalizedBase}/`) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_router_dom.Navigate, {
51
+ to: defaultPath,
52
+ replace: true
53
+ });
54
+ const tabsNav = /* @__PURE__ */ (0, react_jsx_runtime.jsx)("nav", {
55
+ className: "nb-feature-hub__tabs",
56
+ "aria-label": tabsAriaLabel,
57
+ children: visibleTabs.map((tab) => {
58
+ const iconClass = normalizeIcon(tab.icon);
59
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_router_dom.NavLink, {
60
+ to: tab.path,
61
+ className: ({ isActive }) => `nb-feature-hub__tab${isActive ? " nb-feature-hub__tab--active" : ""}`,
62
+ children: [iconClass && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("i", {
63
+ className: iconClass,
64
+ "aria-hidden": "true"
65
+ }), tab.label]
66
+ }, tab.key);
67
+ })
68
+ });
69
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
70
+ className: `nb-feature-hub${densityClass}${className ? ` ${className}` : ""}`,
71
+ children: [
72
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("header", {
73
+ className: "nb-feature-hub__header",
74
+ children: compact ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
75
+ className: "nb-feature-hub__tabs-card",
76
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
77
+ className: "nb-feature-hub__title",
78
+ children: title
79
+ }), tabsNav]
80
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
81
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
82
+ className: "nb-feature-hub__title",
83
+ children: title
84
+ }),
85
+ subtitle && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
86
+ className: "nb-feature-hub__subtitle",
87
+ children: subtitle
88
+ }),
89
+ tabsNav
90
+ ] })
91
+ }),
92
+ showBanner && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
93
+ className: `nb-feature-hub__banner nb-feature-hub__banner--${banner.tone ?? "info"}`,
94
+ role: "status",
95
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("i", {
96
+ className: normalizeIcon(banner.icon ?? "ph-info") ?? "ph ph-info",
97
+ "aria-hidden": "true"
98
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: banner.message })]
99
+ }),
100
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
101
+ className: "nb-feature-hub__content",
102
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_router_dom.Outlet, {})
103
+ })
104
+ ]
105
+ });
106
+ }
107
+ //#endregion
33
108
  //#region packages/admin/AdminHeader.tsx
34
109
  function ActionPopover({ action }) {
35
110
  const { open, toggle, setOpen, containerRef } = (0, _nubitio_ui.useFloatingPanel)();
@@ -1144,6 +1219,7 @@ exports.AdminHeader = AdminHeader;
1144
1219
  exports.AdminShell = AdminShell;
1145
1220
  exports.AdminSidebarMenu = AdminSidebarMenu;
1146
1221
  exports.FeatureGate = FeatureGate;
1222
+ exports.FeatureHubLayout = FeatureHubLayout;
1147
1223
  exports.LoginPage = LoginPage;
1148
1224
  exports.PlanPanel = PlanPanel;
1149
1225
  exports.QuotaUsageBanner = QuotaUsageBanner;
package/dist/index.d.cts CHANGED
@@ -3,6 +3,48 @@ import { FeatureGateProps as FeatureGateProps$1 } from "@nubitio/ui";
3
3
  import { CoreRuntime } from "@nubitio/core";
4
4
  import { QueryClient } from "@tanstack/react-query";
5
5
 
6
+ //#region packages/admin/FeatureHubLayout.d.ts
7
+ interface FeatureHubTab {
8
+ key: string;
9
+ label: string;
10
+ path: string;
11
+ icon?: string;
12
+ visible?: boolean;
13
+ }
14
+ interface FeatureHubBanner {
15
+ message: ReactNode;
16
+ tone?: 'info' | 'warning';
17
+ icon?: string;
18
+ visible?: boolean;
19
+ /** When false, compact density hides the banner to preserve grid space. Default: false */
20
+ showInCompact?: boolean;
21
+ }
22
+ type FeatureHubDensity = 'default' | 'compact';
23
+ interface FeatureHubLayoutProps {
24
+ title: string;
25
+ subtitle?: ReactNode;
26
+ tabs: FeatureHubTab[];
27
+ /** Redirect here when pathname equals basePath exactly. */
28
+ basePath: string;
29
+ defaultPath: string;
30
+ banner?: FeatureHubBanner;
31
+ tabsAriaLabel?: string;
32
+ /** Compact merges title + tabs on one row and hides the subtitle. */
33
+ density?: FeatureHubDensity;
34
+ className?: string;
35
+ }
36
+ declare function FeatureHubLayout({
37
+ title,
38
+ subtitle,
39
+ tabs,
40
+ basePath,
41
+ defaultPath,
42
+ banner,
43
+ tabsAriaLabel,
44
+ density,
45
+ className
46
+ }: FeatureHubLayoutProps): import("react").JSX.Element;
47
+ //#endregion
6
48
  //#region packages/admin/AdminHeader.d.ts
7
49
  interface AdminHeaderAction {
8
50
  id: string;
@@ -444,4 +486,4 @@ declare function hasAnyRole(required: string | string[] | undefined, roles: stri
444
486
  */
445
487
  declare function filterMenuByRoles(items: NubitAppMenuItem[], roles: string[]): AdminMenuItem[];
446
488
  //#endregion
447
- export { AdminHeader, type AdminHeaderAction, type AdminHeaderProps, type AdminMenuItem, type AdminMenuSubItem, AdminShell, type AdminShellProps, AdminSidebarMenu, type AdminSidebarMenuProps, type AdminSidebarMenuSelectEvent, type AppProfile, type CreateNubitAppConfig, FeatureGate, type FeatureGateProps, LoginPage, type LoginPageProps, type NotificationType, type NubitApp, type NubitAppMenuContext, type NubitAppMenuItem, type NubitAppMenuSubItem, type NubitAppRoute, type NubitAppUserMenuContext, type ParsedQuotaError, type PlanDefinition, PlanPanel, type PlanPanelLabels, type PlanPanelProps, QuotaUsageBanner, type QuotaUsageBannerProps, type RegisterField, type RegisterFieldType, RegisterPage, type RegisterPageProps, type RegisterSelectOption, type RuntimeConfig, type RuntimeConfigState, type SessionContextValue, type SessionFeatureEntitlement, type SessionProfile, SessionProvider, type SessionProviderConfig, type SessionState, type SessionTenant, StaticSessionProvider, ToastHost, type ToastHostProps, type ToastItem, type UseQuotaUsageOptions, type UseRuntimeConfigOptions, createNubitApp, filterMenuByRoles, hasAnyRole, parseQuotaError, quotaErrorToastMessage, quotaUsageAboveGrid, useAppRuntime, useFeature, useFeatureConfig, useQuotaUsage, useRuntimeConfig, useScreenSize, useScreenSizeClass, useSession };
489
+ export { AdminHeader, type AdminHeaderAction, type AdminHeaderProps, type AdminMenuItem, type AdminMenuSubItem, AdminShell, type AdminShellProps, AdminSidebarMenu, type AdminSidebarMenuProps, type AdminSidebarMenuSelectEvent, type AppProfile, type CreateNubitAppConfig, FeatureGate, type FeatureGateProps, type FeatureHubBanner, type FeatureHubDensity, FeatureHubLayout, type FeatureHubLayoutProps, type FeatureHubTab, LoginPage, type LoginPageProps, type NotificationType, type NubitApp, type NubitAppMenuContext, type NubitAppMenuItem, type NubitAppMenuSubItem, type NubitAppRoute, type NubitAppUserMenuContext, type ParsedQuotaError, type PlanDefinition, PlanPanel, type PlanPanelLabels, type PlanPanelProps, QuotaUsageBanner, type QuotaUsageBannerProps, type RegisterField, type RegisterFieldType, RegisterPage, type RegisterPageProps, type RegisterSelectOption, type RuntimeConfig, type RuntimeConfigState, type SessionContextValue, type SessionFeatureEntitlement, type SessionProfile, SessionProvider, type SessionProviderConfig, type SessionState, type SessionTenant, StaticSessionProvider, ToastHost, type ToastHostProps, type ToastItem, type UseQuotaUsageOptions, type UseRuntimeConfigOptions, createNubitApp, filterMenuByRoles, hasAnyRole, parseQuotaError, quotaErrorToastMessage, quotaUsageAboveGrid, useAppRuntime, useFeature, useFeatureConfig, useQuotaUsage, useRuntimeConfig, useScreenSize, useScreenSizeClass, useSession };
package/dist/index.d.mts CHANGED
@@ -3,6 +3,48 @@ import { FeatureGateProps as FeatureGateProps$1 } from "@nubitio/ui";
3
3
  import { QueryClient } from "@tanstack/react-query";
4
4
  import { CoreRuntime } from "@nubitio/core";
5
5
 
6
+ //#region packages/admin/FeatureHubLayout.d.ts
7
+ interface FeatureHubTab {
8
+ key: string;
9
+ label: string;
10
+ path: string;
11
+ icon?: string;
12
+ visible?: boolean;
13
+ }
14
+ interface FeatureHubBanner {
15
+ message: ReactNode;
16
+ tone?: 'info' | 'warning';
17
+ icon?: string;
18
+ visible?: boolean;
19
+ /** When false, compact density hides the banner to preserve grid space. Default: false */
20
+ showInCompact?: boolean;
21
+ }
22
+ type FeatureHubDensity = 'default' | 'compact';
23
+ interface FeatureHubLayoutProps {
24
+ title: string;
25
+ subtitle?: ReactNode;
26
+ tabs: FeatureHubTab[];
27
+ /** Redirect here when pathname equals basePath exactly. */
28
+ basePath: string;
29
+ defaultPath: string;
30
+ banner?: FeatureHubBanner;
31
+ tabsAriaLabel?: string;
32
+ /** Compact merges title + tabs on one row and hides the subtitle. */
33
+ density?: FeatureHubDensity;
34
+ className?: string;
35
+ }
36
+ declare function FeatureHubLayout({
37
+ title,
38
+ subtitle,
39
+ tabs,
40
+ basePath,
41
+ defaultPath,
42
+ banner,
43
+ tabsAriaLabel,
44
+ density,
45
+ className
46
+ }: FeatureHubLayoutProps): import("react").JSX.Element;
47
+ //#endregion
6
48
  //#region packages/admin/AdminHeader.d.ts
7
49
  interface AdminHeaderAction {
8
50
  id: string;
@@ -444,4 +486,4 @@ declare function hasAnyRole(required: string | string[] | undefined, roles: stri
444
486
  */
445
487
  declare function filterMenuByRoles(items: NubitAppMenuItem[], roles: string[]): AdminMenuItem[];
446
488
  //#endregion
447
- export { AdminHeader, type AdminHeaderAction, type AdminHeaderProps, type AdminMenuItem, type AdminMenuSubItem, AdminShell, type AdminShellProps, AdminSidebarMenu, type AdminSidebarMenuProps, type AdminSidebarMenuSelectEvent, type AppProfile, type CreateNubitAppConfig, FeatureGate, type FeatureGateProps, LoginPage, type LoginPageProps, type NotificationType, type NubitApp, type NubitAppMenuContext, type NubitAppMenuItem, type NubitAppMenuSubItem, type NubitAppRoute, type NubitAppUserMenuContext, type ParsedQuotaError, type PlanDefinition, PlanPanel, type PlanPanelLabels, type PlanPanelProps, QuotaUsageBanner, type QuotaUsageBannerProps, type RegisterField, type RegisterFieldType, RegisterPage, type RegisterPageProps, type RegisterSelectOption, type RuntimeConfig, type RuntimeConfigState, type SessionContextValue, type SessionFeatureEntitlement, type SessionProfile, SessionProvider, type SessionProviderConfig, type SessionState, type SessionTenant, StaticSessionProvider, ToastHost, type ToastHostProps, type ToastItem, type UseQuotaUsageOptions, type UseRuntimeConfigOptions, createNubitApp, filterMenuByRoles, hasAnyRole, parseQuotaError, quotaErrorToastMessage, quotaUsageAboveGrid, useAppRuntime, useFeature, useFeatureConfig, useQuotaUsage, useRuntimeConfig, useScreenSize, useScreenSizeClass, useSession };
489
+ export { AdminHeader, type AdminHeaderAction, type AdminHeaderProps, type AdminMenuItem, type AdminMenuSubItem, AdminShell, type AdminShellProps, AdminSidebarMenu, type AdminSidebarMenuProps, type AdminSidebarMenuSelectEvent, type AppProfile, type CreateNubitAppConfig, FeatureGate, type FeatureGateProps, type FeatureHubBanner, type FeatureHubDensity, FeatureHubLayout, type FeatureHubLayoutProps, type FeatureHubTab, LoginPage, type LoginPageProps, type NotificationType, type NubitApp, type NubitAppMenuContext, type NubitAppMenuItem, type NubitAppMenuSubItem, type NubitAppRoute, type NubitAppUserMenuContext, type ParsedQuotaError, type PlanDefinition, PlanPanel, type PlanPanelLabels, type PlanPanelProps, QuotaUsageBanner, type QuotaUsageBannerProps, type RegisterField, type RegisterFieldType, RegisterPage, type RegisterPageProps, type RegisterSelectOption, type RuntimeConfig, type RuntimeConfigState, type SessionContextValue, type SessionFeatureEntitlement, type SessionProfile, SessionProvider, type SessionProviderConfig, type SessionState, type SessionTenant, StaticSessionProvider, ToastHost, type ToastHostProps, type ToastItem, type UseQuotaUsageOptions, type UseRuntimeConfigOptions, createNubitApp, filterMenuByRoles, hasAnyRole, parseQuotaError, quotaErrorToastMessage, quotaUsageAboveGrid, useAppRuntime, useFeature, useFeatureConfig, useQuotaUsage, useRuntimeConfig, useScreenSize, useScreenSizeClass, useSession };
package/dist/index.mjs CHANGED
@@ -1,11 +1,86 @@
1
+ import { BrowserRouter, Link, NavLink, Navigate, Outlet, Route, Routes, useLocation, useNavigate } from "react-router-dom";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
1
3
  import React, { createContext, useCallback, useContext, useEffect, useMemo, useState } from "react";
2
- import { BrowserRouter, Link, Navigate, Route, Routes, useLocation, useNavigate } from "react-router-dom";
3
4
  import { Badge, Button, Card, FeatureGate as FeatureGate$1, IconButton, TextField, ThemeProvider, ThemeSwitcher, useFloatingPanel } from "@nubitio/ui";
4
- import { jsx, jsxs } from "react/jsx-runtime";
5
5
  import { QueryClient, QueryClientProvider, useQuery } from "@tanstack/react-query";
6
6
  import { CoreConfigProvider, CoreProvider, MercureProvider, useCoreHttpClient } from "@nubitio/core";
7
7
  import { SmartCrudRolesProvider } from "@nubitio/crud";
8
8
  import { HydraResourceSchemaProvider, HydraResourceStoreProvider, SchemaProvider } from "@nubitio/hydra";
9
+ //#region packages/admin/FeatureHubLayout.tsx
10
+ function normalizeIcon(icon) {
11
+ if (!icon) return void 0;
12
+ if (icon.startsWith("ph ")) return icon;
13
+ if (icon.startsWith("ph-")) return `ph ${icon}`;
14
+ return `ph ph-${icon}`;
15
+ }
16
+ function normalizeBasePath(path) {
17
+ return path.endsWith("/") && path.length > 1 ? path.slice(0, -1) : path;
18
+ }
19
+ function FeatureHubLayout({ title, subtitle, tabs, basePath, defaultPath, banner, tabsAriaLabel = "Secciones", density = "default", className = "" }) {
20
+ const location = useLocation();
21
+ const normalizedBase = normalizeBasePath(basePath);
22
+ const visibleTabs = tabs.filter((tab) => tab.visible !== false);
23
+ const compact = density === "compact";
24
+ const densityClass = compact ? " nb-feature-hub--compact" : "";
25
+ const showBanner = banner && banner.visible !== false && (!compact || banner.showInCompact === true);
26
+ if (location.pathname === normalizedBase || location.pathname === `${normalizedBase}/`) return /* @__PURE__ */ jsx(Navigate, {
27
+ to: defaultPath,
28
+ replace: true
29
+ });
30
+ const tabsNav = /* @__PURE__ */ jsx("nav", {
31
+ className: "nb-feature-hub__tabs",
32
+ "aria-label": tabsAriaLabel,
33
+ children: visibleTabs.map((tab) => {
34
+ const iconClass = normalizeIcon(tab.icon);
35
+ return /* @__PURE__ */ jsxs(NavLink, {
36
+ to: tab.path,
37
+ className: ({ isActive }) => `nb-feature-hub__tab${isActive ? " nb-feature-hub__tab--active" : ""}`,
38
+ children: [iconClass && /* @__PURE__ */ jsx("i", {
39
+ className: iconClass,
40
+ "aria-hidden": "true"
41
+ }), tab.label]
42
+ }, tab.key);
43
+ })
44
+ });
45
+ return /* @__PURE__ */ jsxs("div", {
46
+ className: `nb-feature-hub${densityClass}${className ? ` ${className}` : ""}`,
47
+ children: [
48
+ /* @__PURE__ */ jsx("header", {
49
+ className: "nb-feature-hub__header",
50
+ children: compact ? /* @__PURE__ */ jsxs("div", {
51
+ className: "nb-feature-hub__tabs-card",
52
+ children: [/* @__PURE__ */ jsx("h2", {
53
+ className: "nb-feature-hub__title",
54
+ children: title
55
+ }), tabsNav]
56
+ }) : /* @__PURE__ */ jsxs(Fragment, { children: [
57
+ /* @__PURE__ */ jsx("h2", {
58
+ className: "nb-feature-hub__title",
59
+ children: title
60
+ }),
61
+ subtitle && /* @__PURE__ */ jsx("p", {
62
+ className: "nb-feature-hub__subtitle",
63
+ children: subtitle
64
+ }),
65
+ tabsNav
66
+ ] })
67
+ }),
68
+ showBanner && /* @__PURE__ */ jsxs("div", {
69
+ className: `nb-feature-hub__banner nb-feature-hub__banner--${banner.tone ?? "info"}`,
70
+ role: "status",
71
+ children: [/* @__PURE__ */ jsx("i", {
72
+ className: normalizeIcon(banner.icon ?? "ph-info") ?? "ph ph-info",
73
+ "aria-hidden": "true"
74
+ }), /* @__PURE__ */ jsx("span", { children: banner.message })]
75
+ }),
76
+ /* @__PURE__ */ jsx("div", {
77
+ className: "nb-feature-hub__content",
78
+ children: /* @__PURE__ */ jsx(Outlet, {})
79
+ })
80
+ ]
81
+ });
82
+ }
83
+ //#endregion
9
84
  //#region packages/admin/AdminHeader.tsx
10
85
  function ActionPopover({ action }) {
11
86
  const { open, toggle, setOpen, containerRef } = useFloatingPanel();
@@ -1116,4 +1191,4 @@ function createNubitApp(config) {
1116
1191
  return { App };
1117
1192
  }
1118
1193
  //#endregion
1119
- export { AdminHeader, AdminShell, AdminSidebarMenu, FeatureGate, LoginPage, PlanPanel, QuotaUsageBanner, RegisterPage, SessionProvider, StaticSessionProvider, ToastHost, createNubitApp, filterMenuByRoles, hasAnyRole, parseQuotaError, quotaErrorToastMessage, quotaUsageAboveGrid, useAppRuntime, useFeature, useFeatureConfig, useQuotaUsage, useRuntimeConfig, useScreenSize, useScreenSizeClass, useSession };
1194
+ export { AdminHeader, AdminShell, AdminSidebarMenu, FeatureGate, FeatureHubLayout, LoginPage, PlanPanel, QuotaUsageBanner, RegisterPage, SessionProvider, StaticSessionProvider, ToastHost, createNubitApp, filterMenuByRoles, hasAnyRole, parseQuotaError, quotaErrorToastMessage, quotaUsageAboveGrid, useAppRuntime, useFeature, useFeatureConfig, useQuotaUsage, useRuntimeConfig, useScreenSize, useScreenSizeClass, useSession };
package/dist/style.css CHANGED
@@ -1,3 +1,290 @@
1
+ /* stylelint-disable */
2
+ :root {
3
+ --font-family-sans: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
4
+ --font-family-mono: 'JetBrains Mono', 'SFMono-Regular', 'Menlo', 'Monaco', 'Consolas', monospace;
5
+ --font-family-display: 'Syne', var(--font-family-sans);
6
+ --font-size-xxs: 11px;
7
+ --font-size-xs: 12px;
8
+ --font-size-sm: 13px;
9
+ --font-size-md: 14px;
10
+ --font-size-lg: 16px;
11
+ --font-size-xl: 20px;
12
+ --font-size-2xl: 24px;
13
+ --font-size-3xl: 28px;
14
+ --font-size-4xl: 32px;
15
+ --font-size-5xl: 40px;
16
+ --font-weight-regular: 400;
17
+ --font-weight-medium: 500;
18
+ --font-weight-semibold: 600;
19
+ --font-weight-bold: 700;
20
+ --line-height-tight: 1.25;
21
+ --line-height-normal: 1.5;
22
+ --line-height-relaxed: 1.65;
23
+ --space-1: 4px;
24
+ --space-2: 8px;
25
+ --space-3: 12px;
26
+ --space-4: 16px;
27
+ --space-5: 20px;
28
+ --space-6: 24px;
29
+ --space-7: 32px;
30
+ --space-8: 40px;
31
+ --space-9: 48px;
32
+ --space-10: 64px;
33
+ --radius-xs: 3px;
34
+ --radius-sm: 4px;
35
+ --radius-md: 6px;
36
+ --radius-lg: 8px;
37
+ --radius-xl: 14px;
38
+ --toolbar-height: 36px;
39
+ --row-height: 34px;
40
+ --control-height: 28px;
41
+ --control-height-form: 34px;
42
+ --control-icon-size: 20px;
43
+ --footer-height: 48px;
44
+ --side-panel-min-width: 48px;
45
+ --content-padding: 16px;
46
+ --page-padding: 24px;
47
+ --calendar-width: 292px;
48
+ --toolbar-margin-bottom: var(--content-padding);
49
+ --footer-left-padding: calc((var(--side-panel-min-width) - 16px) / 2);
50
+ --grid-header-height: 34px;
51
+ --grid-filter-height: 28px;
52
+ --grid-cell-padding-x: 10px;
53
+ --grid-cell-padding-y: 6px;
54
+ --grid-row-line-height: 1.35;
55
+ --overlay-bg: rgb(0 0 0 / 42%);
56
+ --transition-fast: 100ms ease;
57
+ --transition-base: 150ms ease;
58
+ --transition-slow: 250ms cubic-bezier(0.4, 0, 0.2, 1);
59
+ --z-dropdown: 100;
60
+ --z-sticky: 200;
61
+ --z-overlay: 300;
62
+ --z-modal: 400;
63
+ --z-popover: 500;
64
+ --z-toast: 600;
65
+ --z-shell-panel: 1500;
66
+ --z-shell-scrim: 1499;
67
+ --z-shell-header: 1501;
68
+ --z-shell-popover: 1700;
69
+ --z-shell-dropdown: 1600;
70
+ --z-dialog: 2000;
71
+ --z-picker-panel: 3000;
72
+ --list-padding-left: 8px;
73
+ --list-padding-vertical: 4px;
74
+ --popup-toolbar-item-spacing: 8px;
75
+ --drop-down-gnrc-button-height: 24px;
76
+ --theme-padding: 8px;
77
+ --button-height-theme-difference: calc((var(--theme-padding) - 10px) / 3);
78
+ --button-height: calc(26px + var(--button-height-theme-difference));
79
+ --nb-admin-header-toolbar-vertical-padding: 10px;
80
+ --toolbar-vertical-padding: 16px;
81
+ }
82
+
83
+ html[data-density=compact] {
84
+ --font-size-xs: 11px;
85
+ --font-size-sm: 12px;
86
+ --font-size-md: 13px;
87
+ --space-1: 2px;
88
+ --space-2: 5px;
89
+ --space-3: 8px;
90
+ --space-4: 12px;
91
+ --space-5: 16px;
92
+ --space-6: 20px;
93
+ --toolbar-height: 30px;
94
+ --row-height: 28px;
95
+ --control-height: 24px;
96
+ --control-height-form: 28px;
97
+ --control-icon-size: 16px;
98
+ --content-padding: 12px;
99
+ --page-padding: 16px;
100
+ --grid-header-height: 28px;
101
+ --grid-filter-height: 24px;
102
+ --grid-cell-padding-x: 8px;
103
+ --grid-cell-padding-y: 4px;
104
+ --grid-row-line-height: 1.3;
105
+ }
106
+
107
+ html[data-density=comfortable] {
108
+ --font-size-xs: 13px;
109
+ --font-size-sm: 14px;
110
+ --font-size-md: 15px;
111
+ --space-1: 6px;
112
+ --space-2: 10px;
113
+ --space-3: 16px;
114
+ --space-4: 20px;
115
+ --space-5: 28px;
116
+ --space-6: 36px;
117
+ --toolbar-height: 44px;
118
+ --row-height: 40px;
119
+ --control-height: 34px;
120
+ --control-height-form: 40px;
121
+ --control-icon-size: 22px;
122
+ --content-padding: 20px;
123
+ --page-padding: 28px;
124
+ --grid-header-height: 40px;
125
+ --grid-filter-height: 34px;
126
+ --grid-cell-padding-x: 14px;
127
+ --grid-cell-padding-y: 8px;
128
+ --grid-row-line-height: 1.4;
129
+ }
130
+
131
+ .nb-feature-hub {
132
+ display: flex;
133
+ flex-direction: column;
134
+ height: 100%;
135
+ min-height: 0;
136
+ background: var(--surface-canvas, var(--surface-0));
137
+ }
138
+
139
+ .nb-feature-hub__header {
140
+ display: flex;
141
+ flex-direction: column;
142
+ gap: 0.35rem;
143
+ padding: 1.5rem 1.5rem 0;
144
+ }
145
+
146
+ .nb-feature-hub__title {
147
+ margin: 0;
148
+ font-size: 1.5rem;
149
+ font-weight: 700;
150
+ letter-spacing: -0.02em;
151
+ color: var(--text-primary);
152
+ }
153
+
154
+ .nb-feature-hub__subtitle {
155
+ margin: 0;
156
+ font-size: 0.875rem;
157
+ color: var(--text-secondary);
158
+ max-width: 36rem;
159
+ }
160
+
161
+ .nb-feature-hub__tabs {
162
+ display: flex;
163
+ flex-wrap: wrap;
164
+ gap: 0.25rem;
165
+ margin: 1.25rem 1.5rem 0;
166
+ padding: 0.25rem;
167
+ background: var(--surface-1);
168
+ border: 1px solid var(--border-subtle, var(--border-color));
169
+ border-radius: 10px;
170
+ box-shadow: var(--shadow-sm);
171
+ }
172
+
173
+ .nb-feature-hub__tab {
174
+ display: inline-flex;
175
+ align-items: center;
176
+ gap: 0.45rem;
177
+ padding: 0.55rem 0.9rem;
178
+ font-size: 0.8125rem;
179
+ font-weight: 600;
180
+ color: var(--text-secondary);
181
+ text-decoration: none;
182
+ border: 1px solid transparent;
183
+ border-radius: 7px;
184
+ transition: color 0.15s, background 0.15s, border-color 0.15s;
185
+ }
186
+ .nb-feature-hub__tab .ph {
187
+ font-size: 1rem;
188
+ opacity: 0.75;
189
+ }
190
+ .nb-feature-hub__tab:hover {
191
+ color: var(--text-primary);
192
+ background: var(--surface-2);
193
+ }
194
+ .nb-feature-hub__tab--active {
195
+ color: var(--accent-color);
196
+ background: var(--accent-subtle, color-mix(in srgb, var(--accent-color) 10%, #fff));
197
+ border-color: color-mix(in srgb, var(--accent-color) 22%, transparent);
198
+ box-shadow: var(--shadow-sm);
199
+ }
200
+ .nb-feature-hub__tab--active .ph {
201
+ opacity: 1;
202
+ }
203
+
204
+ .nb-feature-hub--compact .nb-feature-hub__header {
205
+ padding: 0.65rem 1rem 0;
206
+ }
207
+ .nb-feature-hub--compact .nb-feature-hub__tabs-card {
208
+ align-items: center;
209
+ background: var(--surface-1);
210
+ border: 1px solid var(--border-subtle, var(--border-color));
211
+ border-radius: 10px;
212
+ box-shadow: var(--shadow-sm);
213
+ display: flex;
214
+ flex: 1 1 auto;
215
+ flex-wrap: wrap;
216
+ gap: 0.5rem 1rem;
217
+ justify-content: space-between;
218
+ min-width: 0;
219
+ padding: 0.35rem 0.45rem 0.35rem 0.85rem;
220
+ width: 100%;
221
+ }
222
+ .nb-feature-hub--compact .nb-feature-hub__title {
223
+ font-size: 1.125rem;
224
+ line-height: 1.2;
225
+ margin: 0;
226
+ }
227
+ .nb-feature-hub--compact .nb-feature-hub__tabs {
228
+ flex: 1 1 auto;
229
+ justify-content: flex-end;
230
+ margin: 0;
231
+ padding: 0.15rem;
232
+ border: none;
233
+ border-radius: 8px;
234
+ background: transparent;
235
+ box-shadow: none;
236
+ min-width: min(100%, 520px);
237
+ }
238
+ .nb-feature-hub--compact .nb-feature-hub__tab {
239
+ padding: 0.38rem 0.7rem;
240
+ font-size: 0.75rem;
241
+ }
242
+ .nb-feature-hub--compact .nb-feature-hub__tab .ph {
243
+ font-size: 0.9rem;
244
+ }
245
+ .nb-feature-hub--compact .nb-feature-hub__banner {
246
+ margin: 0.5rem 1rem 0;
247
+ padding: 0.45rem 0.65rem;
248
+ font-size: 0.75rem;
249
+ }
250
+
251
+ .nb-feature-hub__banner {
252
+ display: flex;
253
+ align-items: flex-start;
254
+ gap: 0.6rem;
255
+ margin: 0.85rem 1.5rem 0;
256
+ padding: 0.7rem 0.9rem;
257
+ font-size: 0.8125rem;
258
+ line-height: 1.45;
259
+ border-radius: 8px;
260
+ }
261
+ .nb-feature-hub__banner .ph {
262
+ flex-shrink: 0;
263
+ margin-top: 0.1rem;
264
+ font-size: 1rem;
265
+ }
266
+ .nb-feature-hub__banner--info {
267
+ color: var(--status-info-text, #0369a1);
268
+ background: var(--status-info-bg, #e0f2fe);
269
+ border: 1px solid var(--status-info-border, #7dd3fc);
270
+ }
271
+ .nb-feature-hub__banner--warning {
272
+ color: var(--status-warning-text, #b45309);
273
+ background: var(--status-warning-bg, #fef3c7);
274
+ border: 1px solid var(--status-warning-border, #fcd34d);
275
+ }
276
+
277
+ .nb-feature-hub__content {
278
+ flex: 1;
279
+ min-height: 0;
280
+ overflow: auto;
281
+ display: flex;
282
+ flex-direction: column;
283
+ }
284
+
285
+ .nb-feature-hub--compact .nb-feature-hub__content {
286
+ padding-top: 0.35rem;
287
+ }
1
288
  .nb-admin-shell {
2
289
  display: flex;
3
290
  flex-direction: column;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nubitio/admin",
3
- "version": "0.5.29",
3
+ "version": "0.5.30",
4
4
  "type": "module",
5
5
  "description": "Admin shell layout components: responsive sidebar, header, and screen size utilities for Nubit apps.",
6
6
  "license": "MIT",
@@ -53,9 +53,9 @@
53
53
  "react": "^19.0.0",
54
54
  "react-dom": "^19.0.0",
55
55
  "react-router-dom": "^6.0.0",
56
- "@nubitio/core": "^0.5.29",
57
- "@nubitio/crud": "^0.5.29",
58
- "@nubitio/hydra": "^0.5.29",
59
- "@nubitio/ui": "^0.5.29"
56
+ "@nubitio/core": "^0.5.30",
57
+ "@nubitio/crud": "^0.5.30",
58
+ "@nubitio/hydra": "^0.5.30",
59
+ "@nubitio/ui": "^0.5.30"
60
60
  }
61
61
  }