@s3pweb/shell-ui 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/README.md +337 -0
- package/dist/components/actions-menu.d.ts +45 -0
- package/dist/components/button.d.ts +16 -0
- package/dist/components/ecosystem-mega-panel.d.ts +37 -0
- package/dist/components/hover-card.d.ts +16 -0
- package/dist/components/layout-switcher.d.ts +52 -0
- package/dist/components/partner-cluster.d.ts +111 -0
- package/dist/components/popover.d.ts +24 -0
- package/dist/components/realtime-pulse.d.ts +51 -0
- package/dist/components/tooltip.d.ts +14 -0
- package/dist/components/user-menu.d.ts +36 -0
- package/dist/i18n.d.ts +18 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +1931 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/popover-offsets.d.ts +57 -0
- package/dist/lib/sidebar-layout-context.d.ts +13 -0
- package/dist/lib/use-media-query.d.ts +10 -0
- package/dist/lib/utils.d.ts +2 -0
- package/dist/preset.css +140 -0
- package/dist/shell.d.ts +85 -0
- package/dist/sidebar.d.ts +114 -0
- package/dist/themes/aftral.css +46 -0
- package/dist/themes/s3pweb.css +375 -0
- package/dist/types.d.ts +123 -0
- package/package.json +73 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single source of truth for every popover/tooltip `sideOffset` in shell-ui.
|
|
3
|
+
*
|
|
4
|
+
* The goal: regardless of the trigger element's actual size, every popover
|
|
5
|
+
* lands the same visual distance past the row's edge (top-bar) or past the
|
|
6
|
+
* sidebar's right edge (sidebar). Without these constants, each call site
|
|
7
|
+
* tunes its own number and they drift apart as triggers get added or resized.
|
|
8
|
+
*
|
|
9
|
+
* If you find yourself hardcoding a `sideOffset` somewhere, add a new entry
|
|
10
|
+
* here instead. Document what the trigger's geometry is so the value is
|
|
11
|
+
* recomputable when the trigger changes.
|
|
12
|
+
*/
|
|
13
|
+
/** Visual gap between any popover and the trigger's containing row / column edge. */
|
|
14
|
+
export declare const POPOVER_GAP = 8;
|
|
15
|
+
/**
|
|
16
|
+
* Top-bar popovers (open downward).
|
|
17
|
+
*
|
|
18
|
+
* The header row is 38 px tall (h-10 ghost icon Button). Triggers that fill
|
|
19
|
+
* that height (38 px Button) need offset = `POPOVER_GAP`. Shorter triggers
|
|
20
|
+
* (30 px nav buttons with `my-1`, 30 px partners chip centered in the row)
|
|
21
|
+
* sit 4 px above the row's bottom, so they need offset = `POPOVER_GAP + 4`
|
|
22
|
+
* to land at the same Y.
|
|
23
|
+
*/
|
|
24
|
+
export declare const TOPBAR_OFFSET: {
|
|
25
|
+
/** Trigger fills the row vertically — 38 px ghost icon Button. */
|
|
26
|
+
readonly fullRow: 8;
|
|
27
|
+
/** Trigger is shorter than the row — 30 px nav button or partners chip. */
|
|
28
|
+
readonly shortRow: number;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Sidebar popovers (open to the right).
|
|
32
|
+
*
|
|
33
|
+
* Geometry math (collapsed sidebar = 64 px wide):
|
|
34
|
+
* - Nav-group / nav-item button: `w-full` inside the nav's `p-2`
|
|
35
|
+
* → right edge at `64 - 8 = 56` (no scrollbar) or `64 - 8 - scrollbarWidth`
|
|
36
|
+
* (with scrollbar). The `navGroup` constant assumes no scrollbar; for
|
|
37
|
+
* dynamic compensation use `useSidebarScrollbarWidth()` and add the
|
|
38
|
+
* measured width.
|
|
39
|
+
* - Footer ActionButton (collapsed): 32×32 centered inside the footer's
|
|
40
|
+
* `p-2` → right edge at `8 + (48 - 32) / 2 + 32 = 48`.
|
|
41
|
+
* - User-section avatar: has its own `p-1` padding inside `p-2`, so the
|
|
42
|
+
* visible avatar circle is further left — needs a much higher offset.
|
|
43
|
+
*
|
|
44
|
+
* All values land the popover at `sidebar_right + POPOVER_GAP` = 72.
|
|
45
|
+
*/
|
|
46
|
+
export declare const SIDEBAR_OFFSET: {
|
|
47
|
+
/** Nav-group / nav-item button — collapsed sidebar fly-out. */
|
|
48
|
+
readonly navGroup: 16;
|
|
49
|
+
/** Footer ActionButton — expanded mode (full-width row, right at sidebar_right − 8). */
|
|
50
|
+
readonly actionExpanded: 16;
|
|
51
|
+
/** Footer ActionButton — collapsed mode (centered 32 px button). */
|
|
52
|
+
readonly actionCollapsed: 24;
|
|
53
|
+
/** User-section avatar — expanded mode (full-width row with inner p-1). */
|
|
54
|
+
readonly userExpanded: 48;
|
|
55
|
+
/** User-section avatar — collapsed mode. */
|
|
56
|
+
readonly userCollapsed: 20;
|
|
57
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SidebarLayoutContext — provides live layout measurements from the Sidebar
|
|
3
|
+
* to its descendants. Currently exposes the nav scroll container's vertical
|
|
4
|
+
* scrollbar width so collapsed-mode flyouts can compensate their `sideOffset`
|
|
5
|
+
* and stay anchored at the sidebar's right edge regardless of scrollbar
|
|
6
|
+
* presence.
|
|
7
|
+
*/
|
|
8
|
+
export interface SidebarLayout {
|
|
9
|
+
/** 0 when no scrollbar; ~6–17 px depending on OS / theme when present. */
|
|
10
|
+
scrollbarWidth: number;
|
|
11
|
+
}
|
|
12
|
+
export declare const SidebarLayoutProvider: import('react').Provider<SidebarLayout>;
|
|
13
|
+
export declare function useSidebarLayout(): SidebarLayout;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Subscribe to a CSS media query. Returns `true` when the query matches.
|
|
3
|
+
*
|
|
4
|
+
* Uses `useSyncExternalStore` to bridge `MediaQueryList` to React's
|
|
5
|
+
* concurrent-render-safe subscription model — no `setState` inside `useEffect`
|
|
6
|
+
* and no extra render on mount.
|
|
7
|
+
*
|
|
8
|
+
* SSR-safe: the server snapshot returns `false`.
|
|
9
|
+
*/
|
|
10
|
+
export declare function useMediaQuery(query: string): boolean;
|
package/dist/preset.css
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Neutral defaults for @s3pweb/shell-ui.
|
|
3
|
+
* Import this in your app's CSS if you don't already provide these tokens
|
|
4
|
+
* (shadcn/ui-style apps usually do — in which case this file is unnecessary).
|
|
5
|
+
*
|
|
6
|
+
* @import '@s3pweb/shell-ui/preset.css';
|
|
7
|
+
*
|
|
8
|
+
* Every color token shell-ui's components reference is declared in @theme
|
|
9
|
+
* so Tailwind v4 generates utilities for them (bg-primary, hover:bg-primary/10,
|
|
10
|
+
* text-popover-foreground, etc.). Brand themes override the *values* via the
|
|
11
|
+
* regular cascade; that's enough — Tailwind only cares that the tokens exist
|
|
12
|
+
* at @theme-time.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/* Animation utilities (animate-in, fade-in-0, zoom-in-95, slide-in-from-*).
|
|
16
|
+
The shadcn Tooltip / DropdownMenu / Popover defaults reference these — without
|
|
17
|
+
this import the tooltip would still open, just without the fade/zoom/slide
|
|
18
|
+
transition. */
|
|
19
|
+
@import 'tw-animate-css';
|
|
20
|
+
|
|
21
|
+
/* Bind the `dark:` variant to a `.dark` ancestor (shadcn convention) instead
|
|
22
|
+
of the default `prefers-color-scheme` media query. Without this, a user
|
|
23
|
+
whose OS is set to dark mode would see dark-mode hover/focus colors in the
|
|
24
|
+
storybook even though the app itself is in light mode. */
|
|
25
|
+
@custom-variant dark (&:is(.dark *));
|
|
26
|
+
|
|
27
|
+
@theme {
|
|
28
|
+
--color-background: oklch(1 0 0);
|
|
29
|
+
--color-foreground: oklch(0.145 0 0);
|
|
30
|
+
--color-card: oklch(1 0 0);
|
|
31
|
+
--color-card-foreground: oklch(0.145 0 0);
|
|
32
|
+
--color-popover: oklch(1 0 0);
|
|
33
|
+
--color-popover-foreground: oklch(0.145 0 0);
|
|
34
|
+
--color-primary: oklch(0.205 0 0);
|
|
35
|
+
--color-primary-foreground: oklch(0.985 0 0);
|
|
36
|
+
--color-secondary: oklch(0.97 0 0);
|
|
37
|
+
--color-secondary-foreground: oklch(0.205 0 0);
|
|
38
|
+
--color-muted: oklch(0.97 0 0);
|
|
39
|
+
--color-muted-foreground: oklch(0.5 0 0);
|
|
40
|
+
--color-accent: oklch(0.97 0 0);
|
|
41
|
+
--color-accent-foreground: oklch(0.205 0 0);
|
|
42
|
+
--color-border: oklch(0.922 0 0);
|
|
43
|
+
--color-input: oklch(0.922 0 0);
|
|
44
|
+
--color-ring: oklch(0.708 0 0);
|
|
45
|
+
--color-cta: oklch(0.205 0 0);
|
|
46
|
+
--color-cta-foreground: oklch(0.985 0 0);
|
|
47
|
+
--color-sidebar: oklch(0.97 0 0);
|
|
48
|
+
--color-sidebar-foreground: oklch(0.145 0 0);
|
|
49
|
+
|
|
50
|
+
/* Status tokens — needed for `text-success` / `bg-warning` /
|
|
51
|
+
`text-destructive` utilities used by RealtimePulse and any consumer
|
|
52
|
+
that surfaces success / warning / error states. Neutral defaults;
|
|
53
|
+
brand themes (e.g. s3pweb.css) can override the values. */
|
|
54
|
+
--color-success: oklch(0.62 0.18 145);
|
|
55
|
+
--color-success-foreground: oklch(0.985 0 0);
|
|
56
|
+
--color-warning: oklch(0.7 0.18 65);
|
|
57
|
+
--color-warning-foreground: oklch(0.145 0 0);
|
|
58
|
+
--color-destructive: oklch(0.55 0.22 25);
|
|
59
|
+
--color-destructive-foreground: oklch(0.985 0 0);
|
|
60
|
+
|
|
61
|
+
/* Radius scale — match the s3pweb design system so `rounded-md` etc.
|
|
62
|
+
resolve to the same pixels as the live SaaS shell (Tailwind v4's defaults
|
|
63
|
+
are otherwise sm=2px, md=6px, lg=8px, xl=12px — md would be too round). */
|
|
64
|
+
--radius-sm: 2px;
|
|
65
|
+
--radius-md: 4px;
|
|
66
|
+
--radius-lg: 6px;
|
|
67
|
+
--radius-xl: 8px;
|
|
68
|
+
|
|
69
|
+
/* Inter is the s3pweb / aftral house font; safe to set as default — apps that
|
|
70
|
+
ship their own --font-sans will override since their @theme is loaded later. */
|
|
71
|
+
--font-sans: Inter, ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.dark {
|
|
75
|
+
--color-background: oklch(0.145 0 0);
|
|
76
|
+
--color-foreground: oklch(0.985 0 0);
|
|
77
|
+
--color-card: oklch(0.205 0 0);
|
|
78
|
+
--color-card-foreground: oklch(0.985 0 0);
|
|
79
|
+
--color-popover: oklch(0.205 0 0);
|
|
80
|
+
--color-popover-foreground: oklch(0.985 0 0);
|
|
81
|
+
--color-primary: oklch(0.985 0 0);
|
|
82
|
+
--color-primary-foreground: oklch(0.205 0 0);
|
|
83
|
+
--color-secondary: oklch(0.269 0 0);
|
|
84
|
+
--color-secondary-foreground: oklch(0.985 0 0);
|
|
85
|
+
--color-muted: oklch(0.269 0 0);
|
|
86
|
+
--color-muted-foreground: oklch(0.65 0 0);
|
|
87
|
+
--color-accent: oklch(0.269 0 0);
|
|
88
|
+
--color-accent-foreground: oklch(0.985 0 0);
|
|
89
|
+
--color-border: oklch(0.269 0 0);
|
|
90
|
+
--color-input: oklch(0.269 0 0);
|
|
91
|
+
--color-ring: oklch(0.439 0 0);
|
|
92
|
+
--color-cta: oklch(0.985 0 0);
|
|
93
|
+
--color-cta-foreground: oklch(0.205 0 0);
|
|
94
|
+
--color-sidebar: oklch(0.269 0 0);
|
|
95
|
+
--color-sidebar-foreground: oklch(0.985 0 0);
|
|
96
|
+
|
|
97
|
+
/* Brighter status tokens for dark mode. */
|
|
98
|
+
--color-success: oklch(0.78 0.18 145);
|
|
99
|
+
--color-success-foreground: oklch(0.145 0 0);
|
|
100
|
+
--color-warning: oklch(0.82 0.17 75);
|
|
101
|
+
--color-warning-foreground: oklch(0.145 0 0);
|
|
102
|
+
--color-destructive: oklch(0.68 0.22 25);
|
|
103
|
+
--color-destructive-foreground: oklch(0.145 0 0);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/* Match shared-ui/themes/base.css — make Tailwind border utilities default
|
|
107
|
+
to the theme border token instead of currentColor, paint the body with the
|
|
108
|
+
theme tokens, and reset the cursor on interactive elements so buttons get
|
|
109
|
+
the pointer cursor users expect (Tailwind v4 preflight resets it to default,
|
|
110
|
+
which is jarring on shadcn buttons). */
|
|
111
|
+
@layer base {
|
|
112
|
+
* {
|
|
113
|
+
border-color: var(--color-border);
|
|
114
|
+
outline-color: color-mix(in oklab, var(--color-ring) 50%, transparent);
|
|
115
|
+
}
|
|
116
|
+
body {
|
|
117
|
+
background-color: var(--color-background);
|
|
118
|
+
color: var(--color-foreground);
|
|
119
|
+
}
|
|
120
|
+
button,
|
|
121
|
+
a,
|
|
122
|
+
select,
|
|
123
|
+
summary,
|
|
124
|
+
label,
|
|
125
|
+
[role='button'],
|
|
126
|
+
[role='link'],
|
|
127
|
+
[role='tab'],
|
|
128
|
+
[role='checkbox'],
|
|
129
|
+
[role='radio'],
|
|
130
|
+
[role='switch'],
|
|
131
|
+
[role='menuitem'],
|
|
132
|
+
[role='option'],
|
|
133
|
+
[type='button'],
|
|
134
|
+
[type='submit'],
|
|
135
|
+
[type='reset'],
|
|
136
|
+
[type='checkbox'],
|
|
137
|
+
[type='radio'] {
|
|
138
|
+
cursor: pointer;
|
|
139
|
+
}
|
|
140
|
+
}
|
package/dist/shell.d.ts
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { ShellLocale } from './i18n';
|
|
3
|
+
import { RenderCollapsedTooltip, SidebarUser } from './sidebar';
|
|
4
|
+
import { NavEntry, NavGroup, NavItem, ShellAction } from './types';
|
|
5
|
+
export interface ShellProps {
|
|
6
|
+
/** Navigation entries (groups + leaf items). */
|
|
7
|
+
items: NavEntry[];
|
|
8
|
+
/** 'sidebar' renders a vertical aside on the left; 'top' renders a horizontal bar. */
|
|
9
|
+
navMode?: 'sidebar' | 'top';
|
|
10
|
+
/** Pass to control the mode externally — omit and Shell manages it locally. */
|
|
11
|
+
onNavModeChange?: (mode: 'sidebar' | 'top') => void;
|
|
12
|
+
collapsed?: boolean;
|
|
13
|
+
onCollapseToggle?: () => void;
|
|
14
|
+
collapsedLogo?: ReactNode;
|
|
15
|
+
topSlot?: ReactNode;
|
|
16
|
+
scrollActiveIntoView?: boolean;
|
|
17
|
+
renderCollapsedTooltip?: RenderCollapsedTooltip;
|
|
18
|
+
footer?: ReactNode;
|
|
19
|
+
footerExtra?: ReactNode;
|
|
20
|
+
userSection?: ReactNode;
|
|
21
|
+
logo?: ReactNode;
|
|
22
|
+
user?: SidebarUser;
|
|
23
|
+
onLogout?: () => void;
|
|
24
|
+
theme?: 'light' | 'dark';
|
|
25
|
+
onThemeToggle?: () => void;
|
|
26
|
+
/** Locale for built-in button labels. Default: `fr`. */
|
|
27
|
+
locale?: ShellLocale;
|
|
28
|
+
onItemSelect?: (item: NavItem, group?: NavGroup) => void;
|
|
29
|
+
onGroupToggle?: (group: NavGroup) => void;
|
|
30
|
+
/** Show the theme toggle when `onThemeToggle` is wired. Default: true. */
|
|
31
|
+
enableThemeToggle?: boolean;
|
|
32
|
+
/** Show the nav-mode toggle when `onNavModeChange` is wired (or Shell manages it locally). Default: true. */
|
|
33
|
+
enableNavModeToggle?: boolean;
|
|
34
|
+
/** Show the logout button when `onLogout` is wired. Default: true. */
|
|
35
|
+
enableLogoutButton?: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Show the decorative chevron that signals an icon-only nav entry has
|
|
38
|
+
* sub-items — the collapsed-sidebar `ChevronRight` and the top-bar
|
|
39
|
+
* `ChevronDown` cue. The expanded-sidebar accordion caret is NOT
|
|
40
|
+
* gated by this flag; it always renders because it doubles as the
|
|
41
|
+
* open/close state indicator. Default: false — opt in per-brand when
|
|
42
|
+
* the visual hint is desired.
|
|
43
|
+
*/
|
|
44
|
+
showSubmenuIndicator?: boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Collapse overflowing top-bar nav items, compact-mode actions, and the
|
|
47
|
+
* compact-mode sidebar footer into a single "Plus" / "…" popover when
|
|
48
|
+
* space is tight. Default: true. Pass `false` for brands whose nav fits
|
|
49
|
+
* the viewport at all sizes and prefer a flat chrome with no overflow
|
|
50
|
+
* trigger.
|
|
51
|
+
*/
|
|
52
|
+
enablePlusOverflow?: boolean;
|
|
53
|
+
/** Extra action buttons rendered alongside the built-in cluster (e.g. fullscreen, mobile-mode). */
|
|
54
|
+
actions?: ShellAction[];
|
|
55
|
+
/**
|
|
56
|
+
* Content rendered inside a click-Popover anchored to the user avatar /
|
|
57
|
+
* name section. When set, that section becomes a ghost-button trigger
|
|
58
|
+
* (cursor pointer, hover background). When omitted, the user section
|
|
59
|
+
* stays non-interactive — matches the previous behaviour.
|
|
60
|
+
*
|
|
61
|
+
* The menu node itself is free-form JSX — typically a list of menu
|
|
62
|
+
* items (profile, preferences, help, …). Wrap each row in
|
|
63
|
+
* `<PopoverClose asChild>` so clicking dismisses the menu before
|
|
64
|
+
* running the row's action.
|
|
65
|
+
*/
|
|
66
|
+
userMenu?: ReactNode;
|
|
67
|
+
/** Main content rendered next to the sidebar / under the top bar. */
|
|
68
|
+
children?: ReactNode;
|
|
69
|
+
/** Extra Tailwind classes on the root layout container. */
|
|
70
|
+
className?: string;
|
|
71
|
+
/** Tailwind classes for the main content wrapper. Default: `flex-1 overflow-auto`. */
|
|
72
|
+
mainClassName?: string;
|
|
73
|
+
/** Background class applied to the chrome surface — the `<aside>` in sidebar mode, the `<header>` in top mode. Overrides the defaults (bg-background / bg-sidebar). */
|
|
74
|
+
bgClassName?: string;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Stateless white-label shell that renders either a vertical sidebar or a
|
|
78
|
+
* horizontal top-nav based on `navMode`. The active layout switch is internal,
|
|
79
|
+
* so consumers don't need to compose Sidebar + a custom top-bar themselves.
|
|
80
|
+
*
|
|
81
|
+
* <Shell items={...} navMode='sidebar' user={...}>
|
|
82
|
+
* <Outlet />
|
|
83
|
+
* </Shell>
|
|
84
|
+
*/
|
|
85
|
+
export declare function Shell({ items, navMode: navModeProp, onNavModeChange, collapsed, onCollapseToggle, collapsedLogo, topSlot, scrollActiveIntoView, renderCollapsedTooltip, footer, footerExtra, userSection, logo, user, onLogout, theme, onThemeToggle, locale, onItemSelect, onGroupToggle, enableThemeToggle, enableNavModeToggle, enableLogoutButton, showSubmenuIndicator, enablePlusOverflow, actions, userMenu, children, className, mainClassName, bgClassName, }: ShellProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { ShellLocale } from './i18n';
|
|
3
|
+
import { NavEntry, NavGroup, NavItem, ShellAction } from './types';
|
|
4
|
+
export interface SidebarUser {
|
|
5
|
+
/** Display name (e.g. "Addwyn Lelann"). */
|
|
6
|
+
name: string;
|
|
7
|
+
/** Secondary line shown under the name (e.g. an email or role). */
|
|
8
|
+
subtitle?: string;
|
|
9
|
+
/** Initials shown in the avatar; defaults to first letter of the first two words of `name`. */
|
|
10
|
+
initials?: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Render-prop for wrapping a leaf item or group button in a tooltip when the
|
|
14
|
+
* sidebar is collapsed. Omit to fall back to the native `title` attribute.
|
|
15
|
+
*/
|
|
16
|
+
export type RenderCollapsedTooltip = (props: {
|
|
17
|
+
trigger: ReactNode;
|
|
18
|
+
entry: NavEntry;
|
|
19
|
+
isGroup: boolean;
|
|
20
|
+
onItemSelect?: (item: NavItem, group?: NavGroup) => void;
|
|
21
|
+
}) => ReactNode;
|
|
22
|
+
export interface SidebarProps {
|
|
23
|
+
/** Navigation entries to render. */
|
|
24
|
+
items: NavEntry[];
|
|
25
|
+
/** Collapsed state — controlled. Consumer owns this. */
|
|
26
|
+
collapsed?: boolean;
|
|
27
|
+
/** Called when the user clicks the collapse toggle. Consumer flips its own state. */
|
|
28
|
+
onCollapseToggle?: () => void;
|
|
29
|
+
/** Called when the user clicks a leaf nav item. Consumer wires this to its router. */
|
|
30
|
+
onItemSelect?: (item: NavItem, group?: NavGroup) => void;
|
|
31
|
+
/** Called when the user clicks a group header. Consumer flips group.expanded in its own state. */
|
|
32
|
+
onGroupToggle?: (group: NavGroup) => void;
|
|
33
|
+
/** Logo at the top of the sidebar. */
|
|
34
|
+
logo?: ReactNode;
|
|
35
|
+
/** Optional alternate logo shown when collapsed (smaller variant). */
|
|
36
|
+
collapsedLogo?: ReactNode;
|
|
37
|
+
/** Slot under the logo — typically an entity / structure picker. */
|
|
38
|
+
topSlot?: ReactNode;
|
|
39
|
+
/** When set, renders the built-in user section (avatar + name + subtitle + logout). */
|
|
40
|
+
user?: SidebarUser;
|
|
41
|
+
/** Called when the user clicks the logout button in the built-in user section. */
|
|
42
|
+
onLogout?: () => void;
|
|
43
|
+
/** Current theme. When `onThemeToggle` is provided, renders a Sun/Moon toggle button. */
|
|
44
|
+
theme?: 'light' | 'dark';
|
|
45
|
+
/** Called when the user clicks the theme toggle. */
|
|
46
|
+
onThemeToggle?: () => void;
|
|
47
|
+
/** Current navigation mode. When `onNavModeChange` is provided, renders a PanelLeft/PanelTop toggle. */
|
|
48
|
+
navMode?: 'sidebar' | 'top';
|
|
49
|
+
/** Called with the new mode when the user clicks the nav-mode toggle. */
|
|
50
|
+
onNavModeChange?: (mode: 'sidebar' | 'top') => void;
|
|
51
|
+
/** Show the theme toggle when `onThemeToggle` is wired. Default: true. */
|
|
52
|
+
enableThemeToggle?: boolean;
|
|
53
|
+
/** Show the nav-mode toggle when `onNavModeChange` is wired. Default: true. */
|
|
54
|
+
enableNavModeToggle?: boolean;
|
|
55
|
+
/** Show the logout button when `onLogout` is wired. Default: true. */
|
|
56
|
+
enableLogoutButton?: boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Show the decorative `data-shell-ui-nav-has-children` ChevronRight on
|
|
59
|
+
* collapsed nav groups. The expanded-sidebar accordion caret is NOT
|
|
60
|
+
* gated by this flag — it always renders because it doubles as the
|
|
61
|
+
* open/close indicator. Default: false.
|
|
62
|
+
*/
|
|
63
|
+
showSubmenuIndicator?: boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Collapse the footer into a single "Plus" / "…" popover row below
|
|
66
|
+
* `max-height: 820px`. Default: true. Pass `false` to keep every footer
|
|
67
|
+
* control inline regardless of viewport height.
|
|
68
|
+
*/
|
|
69
|
+
enablePlusOverflow?: boolean;
|
|
70
|
+
/** Extra action buttons rendered in the footer cluster (after theme/nav-mode, before user section). */
|
|
71
|
+
actions?: ShellAction[];
|
|
72
|
+
/** Locale for the built-in button labels (collapse / logout / theme / nav-mode). Default: `fr`. */
|
|
73
|
+
locale?: ShellLocale;
|
|
74
|
+
/** Replace the entire built-in user section with custom content. */
|
|
75
|
+
userSection?: ReactNode;
|
|
76
|
+
/**
|
|
77
|
+
* When set, wraps the avatar+name region in a click-Popover trigger
|
|
78
|
+
* with this content. The region picks up cursor-pointer + ghost-button
|
|
79
|
+
* hover styling. Doesn't apply when `userSection` is set (custom override).
|
|
80
|
+
*/
|
|
81
|
+
userMenu?: ReactNode;
|
|
82
|
+
/** Replace the entire built-in footer (theme + nav-mode + any custom extras) with custom content. */
|
|
83
|
+
footer?: ReactNode;
|
|
84
|
+
/** Extra elements rendered above the built-in theme/nav-mode toggles (e.g. brand-specific actions). */
|
|
85
|
+
footerExtra?: ReactNode;
|
|
86
|
+
/** Auto-scroll the active item into view on first render. Default: true. */
|
|
87
|
+
scrollActiveIntoView?: boolean;
|
|
88
|
+
/** Render-prop for tooltips on collapsed items / groups. */
|
|
89
|
+
renderCollapsedTooltip?: RenderCollapsedTooltip;
|
|
90
|
+
/** Extra classes applied to the root <aside>. */
|
|
91
|
+
className?: string;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Stateless sidebar with built-in feature buttons (user / theme / nav-mode).
|
|
95
|
+
* The consumer owns:
|
|
96
|
+
* - the router (active state is passed in via `item.active` / `group.active`)
|
|
97
|
+
* - the collapsed / expanded / theme / nav-mode state (controlled props + callbacks)
|
|
98
|
+
* - i18n (labels come in as props)
|
|
99
|
+
*
|
|
100
|
+
* Style: assumes Tailwind v4. Import `@s3pweb/shell-ui/preset.css` for neutral
|
|
101
|
+
* defaults or `@s3pweb/shell-ui/themes/{s3pweb,aftral}.css` for a brand theme.
|
|
102
|
+
*/
|
|
103
|
+
export declare function Sidebar({ items, collapsed: collapsedProp, onCollapseToggle, onItemSelect, onGroupToggle, logo, collapsedLogo, topSlot, user, onLogout, theme, onThemeToggle, navMode, onNavModeChange, enableThemeToggle, enableNavModeToggle, enableLogoutButton, showSubmenuIndicator, enablePlusOverflow, actions, locale, userSection, userMenu, footer, footerExtra, scrollActiveIntoView, renderCollapsedTooltip, className, }: SidebarProps): import("react/jsx-runtime").JSX.Element;
|
|
104
|
+
export interface SidebarItemsProps {
|
|
105
|
+
items: NavEntry[];
|
|
106
|
+
collapsed?: boolean;
|
|
107
|
+
onItemSelect?: (item: NavItem, group?: NavGroup) => void;
|
|
108
|
+
onGroupToggle?: (group: NavGroup) => void;
|
|
109
|
+
scrollActiveIntoView?: boolean;
|
|
110
|
+
renderCollapsedTooltip?: RenderCollapsedTooltip;
|
|
111
|
+
/** Show the decorative chevron on collapsed groups. The accordion caret (expanded mode) is always shown. Default: false. */
|
|
112
|
+
showSubmenuIndicator?: boolean;
|
|
113
|
+
}
|
|
114
|
+
export declare function SidebarItems({ items, collapsed, onItemSelect, onGroupToggle, scrollActiveIntoView, renderCollapsedTooltip, showSubmenuIndicator, }: SidebarItemsProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @s3pweb/shell-ui — AFTRAL brand theme.
|
|
3
|
+
*
|
|
4
|
+
* Activated by adding `data-shell-theme="aftral"` on any ancestor of the
|
|
5
|
+
* shell-ui components. Pairs with the `.dark` class for dark mode.
|
|
6
|
+
*
|
|
7
|
+
* @import '@s3pweb/shell-ui/themes/aftral.css';
|
|
8
|
+
* <html class='dark' data-shell-theme='aftral'>...</html>
|
|
9
|
+
*
|
|
10
|
+
* Source palette: apps/aftral/src/theme/aftral.css (AFTRAL blue + green accent).
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
[data-shell-theme='aftral'] {
|
|
14
|
+
--color-background: #ffffff;
|
|
15
|
+
--color-foreground: #123c5d;
|
|
16
|
+
--color-card: #ffffff;
|
|
17
|
+
--color-card-foreground: #123c5d;
|
|
18
|
+
--color-accent: #e3e8ec;
|
|
19
|
+
--color-accent-foreground: #123c5d;
|
|
20
|
+
--color-border: #d4dce2;
|
|
21
|
+
--color-muted-foreground: #4d6d86;
|
|
22
|
+
|
|
23
|
+
--color-primary: #123c5d;
|
|
24
|
+
--color-primary-foreground: #ffffff;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
[data-shell-theme='aftral'].dark,
|
|
28
|
+
.dark [data-shell-theme='aftral'] {
|
|
29
|
+
--color-background: oklch(0.22 0.015 250);
|
|
30
|
+
--color-foreground: oklch(0.93 0.008 240);
|
|
31
|
+
--color-card: oklch(0.26 0.015 250);
|
|
32
|
+
--color-card-foreground: oklch(0.93 0.008 240);
|
|
33
|
+
--color-accent: oklch(0.3 0.03 240);
|
|
34
|
+
--color-accent-foreground: oklch(0.93 0.008 240);
|
|
35
|
+
--color-border: oklch(0.34 0.02 240);
|
|
36
|
+
--color-muted-foreground: oklch(0.65 0.015 240);
|
|
37
|
+
|
|
38
|
+
--color-primary: oklch(0.72 0.16 142);
|
|
39
|
+
--color-primary-foreground: oklch(0.16 0.03 240);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/* AFTRAL decoration: solid primary background on active nav items. */
|
|
43
|
+
[data-shell-theme='aftral'] [data-shell-ui-sidebar] [data-shell-ui-nav-item][data-active] {
|
|
44
|
+
background: var(--color-primary);
|
|
45
|
+
color: var(--color-primary-foreground);
|
|
46
|
+
}
|