@djangocfg/layouts 2.1.319 → 2.1.321
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 +41 -102
- package/package.json +27 -18
- package/src/configurator/index.ts +14 -0
- package/src/configurator/private/index.ts +6 -0
- package/src/configurator/private/schema.ts +190 -0
- package/src/layouts/PrivateLayout/PrivateLayout.tsx +46 -1
- package/src/layouts/PrivateLayout/README.md +129 -0
- package/src/layouts/PrivateLayout/components/PrivateContent.tsx +1 -1
- package/src/layouts/PrivateLayout/components/PrivateSidebar.tsx +214 -47
- package/src/layouts/PrivateLayout/index.ts +10 -1
- package/src/layouts/_components/PrivateSidebarAccount.tsx +284 -146
- package/src/layouts/_components/SidebarFeatured.tsx +70 -0
package/README.md
CHANGED
|
@@ -23,115 +23,68 @@ Peers: `@djangocfg/ui-core`, `@djangocfg/ui-nextjs`, React 19, Next.js 16+, Tail
|
|
|
23
23
|
|
|
24
24
|
---
|
|
25
25
|
|
|
26
|
-
##
|
|
26
|
+
## Quick start
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
```tsx
|
|
31
|
-
<BaseApp
|
|
32
|
-
project="my-app"
|
|
33
|
-
theme={{ defaultTheme: 'system', storageKey: 'my-theme' }}
|
|
34
|
-
auth={{ apiUrl: process.env.NEXT_PUBLIC_API_URL }}
|
|
35
|
-
>
|
|
36
|
-
{children}
|
|
37
|
-
</BaseApp>
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
| Prop | Role |
|
|
41
|
-
|---|---|
|
|
42
|
-
| `project` | Name for monitor / debug. Enables `window.monitor`. |
|
|
43
|
-
| `theme` | `defaultTheme`, `storageKey`, and optional **`style`** (preset + CSS-var overrides). |
|
|
44
|
-
| `auth`, `analytics`, `centrifugo`, `errorTracking`, `errorBoundary`, `swr`, `pwaInstall`, `monitor`, `debug` | Optional integrations. |
|
|
45
|
-
|
|
46
|
-
### `theme.style`
|
|
47
|
-
|
|
48
|
-
Mounts `ThemeStyleBridge` → injects `<style id="djangocfg-baseapp-theme-style">` with `--*` variables. Merge order: imported globals → preset → `vars` (strongest).
|
|
49
|
-
|
|
50
|
-
| Piece | Meaning |
|
|
51
|
-
|---|---|
|
|
52
|
-
| `preset` | One of `THEME_STYLE_PRESET_ORDER`. Bundles live in `THEME_STYLE_PRESETS`. |
|
|
53
|
-
| `vars.light` / `vars.dark` | Partial `ThemeCssVarMap` — HSL triplets (`192 90% 35%`); `radius` accepts any CSS length. |
|
|
54
|
-
|
|
55
|
-
Presets: `default` · `django-cfg` · `ios` · `soft` · `dense` · `high-contrast`.
|
|
56
|
-
|
|
57
|
-
Playground-only buckets (shadows, typography, spacing) are **not** injected — export full CSS from the Theme Configurator when you need them.
|
|
58
|
-
|
|
59
|
-
Exports: `ThemeStyleConfig`, `ThemeCssVarKey`, `ThemeCssVarMap`, `ThemeStylePresetId`, `THEME_STYLE_PRESETS`, `THEME_STYLE_PRESET_ORDER`, `buildThemeStyleSheet`, `ThemeStyleBridge`.
|
|
60
|
-
|
|
61
|
-
---
|
|
62
|
-
|
|
63
|
-
## AppLayout
|
|
64
|
-
|
|
65
|
-
Wraps `BaseApp` and picks **admin → private → public** layout by path (`matchesPath` / `enabledPath`). `noLayoutPaths` skips the shell entirely (fullscreen / embeds). `publicChrome` merges navbar/footer/main spacing defaults.
|
|
28
|
+
`AppLayout` wraps `BaseApp` (theme, auth, analytics, SWR, toasts) and routes the page to the right shell based on path:
|
|
66
29
|
|
|
67
30
|
```tsx
|
|
68
31
|
<AppLayout
|
|
69
32
|
layouts={{
|
|
70
|
-
public: { component: PublicLayout, enabledPath: ['/', '/legal'
|
|
33
|
+
public: { component: PublicLayout, enabledPath: ['/', '/legal'] },
|
|
71
34
|
private: { component: PrivateLayout, enabledPath: ['/dashboard'] },
|
|
72
35
|
admin: { component: AdminLayout, enabledPath: '/admin' },
|
|
73
|
-
noLayoutPaths: ['/embed'
|
|
36
|
+
noLayoutPaths: ['/embed'],
|
|
74
37
|
}}
|
|
75
38
|
baseApp={{ project: 'my-app', theme: { defaultTheme: 'system' }, auth: { apiUrl: '…' } }}
|
|
76
|
-
i18n={{ locale, locales, onLocaleChange }}
|
|
39
|
+
i18n={{ locale, locales, onLocaleChange: changeLocale, routing }}
|
|
77
40
|
>
|
|
78
41
|
{children}
|
|
79
42
|
</AppLayout>
|
|
80
43
|
```
|
|
81
44
|
|
|
82
|
-
|
|
45
|
+
Use `BaseApp` directly when you don't need route-based layout switching — see [AppLayout README](./src/layouts/AppLayout/README.md) for the matching rules, `noLayoutPaths`, `publicChrome`, and `i18n.routing` plumbing.
|
|
46
|
+
|
|
47
|
+
> **Pass `i18n`** when using `next-intl` or any locale-prefixed routing. Without it, the path matcher can mis-strip 2-letter segments (e.g. `/ui/*` treated as locale `ui`).
|
|
83
48
|
|
|
84
49
|
---
|
|
85
50
|
|
|
86
51
|
## Layouts
|
|
87
52
|
|
|
88
|
-
| Component | Use |
|
|
89
|
-
|
|
90
|
-
| **`PublicLayout`** | Marketing / docs. Slots for navbar
|
|
91
|
-
| **`PrivateLayout`** |
|
|
92
|
-
| **`AuthLayout`** | Sign-in flows. |
|
|
93
|
-
| **`AdminLayout`** | Admin console. |
|
|
94
|
-
| **`ProfileLayout`** | Profile page —
|
|
53
|
+
| Component | Use | Docs |
|
|
54
|
+
|---|---|---|
|
|
55
|
+
| **`PublicLayout`** | Marketing / docs. Slots for navbar (`Floating`/`Flush`/`Minimal`) + footer + locale + auth controls. | [README](./src/layouts/PublicLayout/README.md) |
|
|
56
|
+
| **`PrivateLayout`** | Authenticated app shell — sidebar (collapsible icon rail, accordion groups, rail/featured/CTA slots) + popover account footer. | [README](./src/layouts/PrivateLayout/README.md) |
|
|
57
|
+
| **`AuthLayout`** | Sign-in / sign-up flows. | [README](./src/layouts/AuthLayout/README.md) |
|
|
58
|
+
| **`AdminLayout`** | Admin console. | — |
|
|
59
|
+
| **`ProfileLayout`** | Profile page — see below. | |
|
|
95
60
|
|
|
96
|
-
### `
|
|
61
|
+
### `ProfileLayout`
|
|
97
62
|
|
|
98
63
|
```tsx
|
|
99
|
-
<
|
|
100
|
-
sidebar={sidebar}
|
|
101
|
-
header={header}
|
|
102
|
-
visual={{ variant: 'boxed', inset: 12, radius: '2xl', border: true }}
|
|
103
|
-
>
|
|
104
|
-
{children}
|
|
105
|
-
</PrivateLayout>
|
|
64
|
+
<ProfileLayout enable2FA enableDeleteAccount tabs={tabs} slots={slots} />
|
|
106
65
|
```
|
|
107
66
|
|
|
108
|
-
|
|
109
|
-
|
|
67
|
+
| Prop | Role |
|
|
68
|
+
|---|---|
|
|
69
|
+
| `enable2FA` | Show Security tab with 2FA management. |
|
|
70
|
+
| `enableDeleteAccount` | Show Delete account in `⋯` menu. |
|
|
71
|
+
| `tabs` | Extra `ProfileTab[]` appended after built-ins. |
|
|
72
|
+
| `slots.headerBadge` / `headerMenuItems` / `headerAfter` / `footer` | Slot content around the avatar row, menu, and tab body. |
|
|
110
73
|
|
|
111
|
-
|
|
112
|
-
|---|---|---|---|
|
|
113
|
-
| `variant` | `'full-bleed' \| 'boxed'` | `'boxed'` | Switch between the two shells. |
|
|
114
|
-
| `inset` | `number \| { x?: number; y?: number }` | `12` | Gap (px) between the card and the viewport edges (md+). |
|
|
115
|
-
| `radius` | `'sm' \| 'md' \| 'lg' \| 'xl' \| '2xl' \| '3xl'` | `'2xl'` | Card corner radius. |
|
|
116
|
-
| `background` | `'sidebar' \| 'muted' \| 'card' \| 'background'` | `'sidebar'` | Canvas colour painted *behind* the boxed card. |
|
|
117
|
-
| `border` | `boolean` | `true` | 1px border on the card. |
|
|
118
|
-
| `maxWidth` | `'none' \| '7xl' \| 'screen-xl' \| 'screen-2xl'` | `'none'` | Optional inner content width cap. |
|
|
74
|
+
---
|
|
119
75
|
|
|
120
|
-
|
|
76
|
+
## Theme
|
|
121
77
|
|
|
122
|
-
|
|
123
|
-
<ProfileLayout enable2FA enableDeleteAccount tabs={tabs} slots={slots} />
|
|
124
|
-
```
|
|
78
|
+
`baseApp.theme.style` injects `<style id="djangocfg-baseapp-theme-style">` with `--*` CSS variables. Merge order: imported globals → preset → `vars` (strongest).
|
|
125
79
|
|
|
126
|
-
|
|
|
127
|
-
|
|
128
|
-
| `
|
|
129
|
-
| `
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
| `slots.footer` | `ReactNode` | Below all tab content. |
|
|
80
|
+
| Piece | Meaning |
|
|
81
|
+
|---|---|
|
|
82
|
+
| `preset` | `default` · `django-cfg` · `ios` · `soft` · `dense` · `high-contrast`. |
|
|
83
|
+
| `vars.light` / `vars.dark` | Partial `ThemeCssVarMap` — HSL triplets (`192 90% 35%`); `radius` accepts any CSS length. |
|
|
84
|
+
|
|
85
|
+
Playground-only buckets (shadows, typography, spacing) are **not** injected — export full CSS from the Theme Configurator when you need them.
|
|
86
|
+
|
|
87
|
+
Exports: `ThemeStyleConfig`, `ThemeCssVarKey`, `ThemeCssVarMap`, `ThemeStylePresetId`, `THEME_STYLE_PRESETS`, `THEME_STYLE_PRESET_ORDER`, `buildThemeStyleSheet`, `ThemeStyleBridge`.
|
|
135
88
|
|
|
136
89
|
---
|
|
137
90
|
|
|
@@ -142,29 +95,16 @@ import { useLocaleSwitcher } from '@djangocfg/nextjs/i18n/client';
|
|
|
142
95
|
import { routing } from '@djangocfg/nextjs/i18n/routing';
|
|
143
96
|
|
|
144
97
|
const { locale, locales, changeLocale } = useLocaleSwitcher();
|
|
145
|
-
<AppLayout i18n={{ locale, locales, onLocaleChange: changeLocale, routing }}>
|
|
146
|
-
{children}
|
|
147
|
-
</AppLayout>
|
|
98
|
+
<AppLayout i18n={{ locale, locales, onLocaleChange: changeLocale, routing }}>...</AppLayout>
|
|
148
99
|
```
|
|
149
100
|
|
|
150
|
-
`
|
|
101
|
+
When `routing` is set, `BaseApp` mounts a locale-aware `<Link>` adapter so every layout-rendered link keeps the active locale prefix. Drop it for default-locale-only apps.
|
|
151
102
|
|
|
152
103
|
---
|
|
153
104
|
|
|
154
105
|
## Monitor & debug
|
|
155
106
|
|
|
156
|
-
`project`
|
|
157
|
-
|
|
158
|
-
---
|
|
159
|
-
|
|
160
|
-
## Utilities
|
|
161
|
-
|
|
162
|
-
| API | Role |
|
|
163
|
-
|---|---|
|
|
164
|
-
| `useErrorEmitter`, `emitRuntimeError` | Error tracking hooks. |
|
|
165
|
-
| `ErrorLayout` (`/components/errors`) | 404 / error pages. |
|
|
166
|
-
| `RedirectPage` | Auth redirect helper. |
|
|
167
|
-
| `PrivacyPage`, `TermsPage`, … (`/pages/legal`) | Legal templates. |
|
|
107
|
+
`baseApp.project` enables `window.monitor`. Debug panel: `Cmd+D` or `?debug=1`.
|
|
168
108
|
|
|
169
109
|
---
|
|
170
110
|
|
|
@@ -174,13 +114,12 @@ const { locale, locales, changeLocale } = useLocaleSwitcher();
|
|
|
174
114
|
|---|---|
|
|
175
115
|
| `@djangocfg/layouts` | Barrel. |
|
|
176
116
|
| `@djangocfg/layouts/layouts` | Layout components. |
|
|
177
|
-
| `@djangocfg/layouts/components` | Misc
|
|
178
|
-
| `@djangocfg/layouts/pages/legal` | Legal
|
|
117
|
+
| `@djangocfg/layouts/components` | Misc + `ErrorLayout`, `RedirectPage`. |
|
|
118
|
+
| `@djangocfg/layouts/pages/legal` | Legal page templates. |
|
|
119
|
+
| `@djangocfg/layouts/configurator` | JSON Schemas for `<JsonSchemaForm>`-driven configurator UIs (PrivateLayout for now). Pair with `@djangocfg/ui-tools`. |
|
|
179
120
|
| `@djangocfg/layouts/styles` | Base CSS. |
|
|
180
121
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
`@djangocfg/ext-newsletter`, `ext-knowbase`, `ext-leads`, `ext-payments`, `ext-support`, …
|
|
122
|
+
Extensions: `@djangocfg/ext-newsletter`, `ext-knowbase`, `ext-leads`, `ext-payments`, `ext-support`, …
|
|
184
123
|
|
|
185
124
|
## License
|
|
186
125
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@djangocfg/layouts",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.321",
|
|
4
4
|
"description": "Simple, straightforward layout components for Next.js - import and use with props",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"layouts",
|
|
@@ -60,6 +60,16 @@
|
|
|
60
60
|
"import": "./src/pages/index.ts",
|
|
61
61
|
"require": "./src/pages/index.ts"
|
|
62
62
|
},
|
|
63
|
+
"./configurator": {
|
|
64
|
+
"types": "./src/configurator/index.ts",
|
|
65
|
+
"import": "./src/configurator/index.ts",
|
|
66
|
+
"require": "./src/configurator/index.ts"
|
|
67
|
+
},
|
|
68
|
+
"./configurator/private": {
|
|
69
|
+
"types": "./src/configurator/private/index.ts",
|
|
70
|
+
"import": "./src/configurator/private/index.ts",
|
|
71
|
+
"require": "./src/configurator/private/index.ts"
|
|
72
|
+
},
|
|
63
73
|
"./styles": "./src/styles/index.css",
|
|
64
74
|
"./styles/dashboard": "./src/styles/dashboard.css"
|
|
65
75
|
},
|
|
@@ -74,14 +84,13 @@
|
|
|
74
84
|
"check": "tsc --noEmit"
|
|
75
85
|
},
|
|
76
86
|
"peerDependencies": {
|
|
77
|
-
"@djangocfg/api": "^2.1.
|
|
78
|
-
"@djangocfg/centrifugo": "^2.1.
|
|
79
|
-
"@djangocfg/debuger": "^2.1.
|
|
80
|
-
"@djangocfg/i18n": "^2.1.
|
|
81
|
-
"@djangocfg/monitor": "^2.1.
|
|
82
|
-
"@djangocfg/ui-core": "^2.1.
|
|
83
|
-
"@djangocfg/ui-nextjs": "^2.1.
|
|
84
|
-
"@djangocfg/ui-tools": "^2.1.319",
|
|
87
|
+
"@djangocfg/api": "^2.1.321",
|
|
88
|
+
"@djangocfg/centrifugo": "^2.1.321",
|
|
89
|
+
"@djangocfg/debuger": "^2.1.321",
|
|
90
|
+
"@djangocfg/i18n": "^2.1.321",
|
|
91
|
+
"@djangocfg/monitor": "^2.1.321",
|
|
92
|
+
"@djangocfg/ui-core": "^2.1.321",
|
|
93
|
+
"@djangocfg/ui-nextjs": "^2.1.321",
|
|
85
94
|
"@hookform/resolvers": "^5.2.2",
|
|
86
95
|
"consola": "^3.4.2",
|
|
87
96
|
"lucide-react": "^0.545.0",
|
|
@@ -111,15 +120,15 @@
|
|
|
111
120
|
"uuid": "^11.1.0"
|
|
112
121
|
},
|
|
113
122
|
"devDependencies": {
|
|
114
|
-
"@djangocfg/api": "^2.1.
|
|
115
|
-
"@djangocfg/centrifugo": "^2.1.
|
|
116
|
-
"@djangocfg/debuger": "^2.1.
|
|
117
|
-
"@djangocfg/i18n": "^2.1.
|
|
118
|
-
"@djangocfg/monitor": "^2.1.
|
|
119
|
-
"@djangocfg/typescript-config": "^2.1.
|
|
120
|
-
"@djangocfg/ui-core": "^2.1.
|
|
121
|
-
"@djangocfg/ui-nextjs": "^2.1.
|
|
122
|
-
"@djangocfg/ui-tools": "^2.1.
|
|
123
|
+
"@djangocfg/api": "^2.1.321",
|
|
124
|
+
"@djangocfg/centrifugo": "^2.1.321",
|
|
125
|
+
"@djangocfg/debuger": "^2.1.321",
|
|
126
|
+
"@djangocfg/i18n": "^2.1.321",
|
|
127
|
+
"@djangocfg/monitor": "^2.1.321",
|
|
128
|
+
"@djangocfg/typescript-config": "^2.1.321",
|
|
129
|
+
"@djangocfg/ui-core": "^2.1.321",
|
|
130
|
+
"@djangocfg/ui-nextjs": "^2.1.321",
|
|
131
|
+
"@djangocfg/ui-tools": "^2.1.321",
|
|
123
132
|
"@types/node": "^24.7.2",
|
|
124
133
|
"@types/react": "^19.1.0",
|
|
125
134
|
"@types/react-dom": "^19.1.0",
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSON Schema descriptors for `<JsonSchemaForm>`-driven layout configurators.
|
|
3
|
+
*
|
|
4
|
+
* Drop a schema + uiSchema into `@djangocfg/ui-tools`'s `<JsonSchemaForm>` and
|
|
5
|
+
* you get a ready-made sidebar that edits the matching `<Layout>` props live.
|
|
6
|
+
*
|
|
7
|
+
* Public surface today:
|
|
8
|
+
* - `private` — `PrivateLayout` configurator (shell + sidebar + header)
|
|
9
|
+
*
|
|
10
|
+
* Public + theme configurators are not exported yet — they currently live in
|
|
11
|
+
* the demo app while the schema shape is iterated on.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export * from './private';
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSON Schema + uiSchema for a `PrivateLayout` configurator UI.
|
|
3
|
+
*
|
|
4
|
+
* The shape mirrors the real `PrivateLayout` runtime API:
|
|
5
|
+
* - `shell.*` → `visual` prop (`LayoutVisualConfig`)
|
|
6
|
+
* - `sidebar.*` → flags consumed by `SidebarConfig` (caller still maps these
|
|
7
|
+
* into a real `SidebarConfig` with concrete `groups`)
|
|
8
|
+
* - `header.*` → fields consumed by `HeaderConfig`
|
|
9
|
+
*
|
|
10
|
+
* Pair with `<JsonSchemaForm density="compact" schema={...} uiSchema={...}>`
|
|
11
|
+
* from `@djangocfg/ui-tools` to get a ready-made sidebar configurator. The
|
|
12
|
+
* `defaultPrivateLayoutConfiguratorData` is a safe initial value that already
|
|
13
|
+
* matches the schema (no nullable fields, no missing keys).
|
|
14
|
+
*
|
|
15
|
+
* Playground-only knobs (sample item count, banner toggles, etc.) live in the
|
|
16
|
+
* consuming app, not here — this stays a clean view of the real layout API.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import type { CustomJsonSchema7, CustomJsonUiSchema7 } from '@djangocfg/ui-core/lib';
|
|
20
|
+
|
|
21
|
+
/** Strongly-typed mirror of the schema — feed this back into PrivateLayout. */
|
|
22
|
+
export interface PrivateLayoutConfiguratorData {
|
|
23
|
+
shell: {
|
|
24
|
+
variant: 'full-bleed' | 'boxed';
|
|
25
|
+
inset: number;
|
|
26
|
+
radius: 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl';
|
|
27
|
+
background: 'sidebar' | 'muted' | 'card' | 'background';
|
|
28
|
+
border: boolean;
|
|
29
|
+
maxWidth: 'none' | '7xl' | 'screen-xl' | 'screen-2xl';
|
|
30
|
+
};
|
|
31
|
+
sidebar: {
|
|
32
|
+
activeIndicator: 'background' | 'rail' | 'both';
|
|
33
|
+
groupLabelStyle: 'uppercase' | 'plain';
|
|
34
|
+
collapsibleGroups: boolean;
|
|
35
|
+
showFeatured: boolean;
|
|
36
|
+
};
|
|
37
|
+
header: {
|
|
38
|
+
userPlan: string;
|
|
39
|
+
showSecondaryAction: boolean;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const privateLayoutConfiguratorSchema: CustomJsonSchema7 = {
|
|
44
|
+
type: 'object',
|
|
45
|
+
properties: {
|
|
46
|
+
shell: {
|
|
47
|
+
type: 'object',
|
|
48
|
+
title: 'Shell',
|
|
49
|
+
properties: {
|
|
50
|
+
variant: {
|
|
51
|
+
type: 'string',
|
|
52
|
+
title: 'Variant',
|
|
53
|
+
enum: ['full-bleed', 'boxed'],
|
|
54
|
+
description: '`boxed` paints a sidebar-coloured canvas around a rounded content card.',
|
|
55
|
+
},
|
|
56
|
+
inset: {
|
|
57
|
+
type: 'integer',
|
|
58
|
+
title: 'Inset (md+)',
|
|
59
|
+
minimum: 0,
|
|
60
|
+
maximum: 32,
|
|
61
|
+
description: 'Gap between the card and viewport edges.',
|
|
62
|
+
},
|
|
63
|
+
radius: {
|
|
64
|
+
type: 'string',
|
|
65
|
+
title: 'Radius',
|
|
66
|
+
enum: ['sm', 'md', 'lg', 'xl', '2xl', '3xl'],
|
|
67
|
+
},
|
|
68
|
+
background: {
|
|
69
|
+
type: 'string',
|
|
70
|
+
title: 'Canvas background',
|
|
71
|
+
enum: ['sidebar', 'muted', 'card', 'background'],
|
|
72
|
+
description: 'Colour painted behind the boxed card.',
|
|
73
|
+
},
|
|
74
|
+
border: {
|
|
75
|
+
type: 'boolean',
|
|
76
|
+
title: 'Border on card',
|
|
77
|
+
description: '1px hairline border around the boxed card.',
|
|
78
|
+
},
|
|
79
|
+
maxWidth: {
|
|
80
|
+
type: 'string',
|
|
81
|
+
title: 'Inner max-width',
|
|
82
|
+
enum: ['none', '7xl', 'screen-xl', 'screen-2xl'],
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
sidebar: {
|
|
87
|
+
type: 'object',
|
|
88
|
+
title: 'Sidebar',
|
|
89
|
+
properties: {
|
|
90
|
+
activeIndicator: {
|
|
91
|
+
type: 'string',
|
|
92
|
+
title: 'Active indicator',
|
|
93
|
+
enum: ['background', 'rail', 'both'],
|
|
94
|
+
description: 'Visual treatment for the active nav item.',
|
|
95
|
+
},
|
|
96
|
+
groupLabelStyle: {
|
|
97
|
+
type: 'string',
|
|
98
|
+
title: 'Group label style',
|
|
99
|
+
enum: ['uppercase', 'plain'],
|
|
100
|
+
description: 'Collapsible groups always render `plain` regardless of this setting.',
|
|
101
|
+
},
|
|
102
|
+
collapsibleGroups: {
|
|
103
|
+
type: 'boolean',
|
|
104
|
+
title: 'Collapsible groups',
|
|
105
|
+
description: 'Mailersend-style accordion: label becomes a clickable trigger.',
|
|
106
|
+
},
|
|
107
|
+
showFeatured: {
|
|
108
|
+
type: 'boolean',
|
|
109
|
+
title: 'Featured CTA tile',
|
|
110
|
+
description: 'Accent-tinted tile rendered below groups.',
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
header: {
|
|
115
|
+
type: 'object',
|
|
116
|
+
title: 'Header / account footer',
|
|
117
|
+
properties: {
|
|
118
|
+
userPlan: {
|
|
119
|
+
type: 'string',
|
|
120
|
+
title: 'User plan',
|
|
121
|
+
enum: ['', 'Free plan', 'Pro plan', 'Max plan', 'Enterprise'],
|
|
122
|
+
description: 'Subtitle under the display name in the footer trigger. Empty hides it.',
|
|
123
|
+
},
|
|
124
|
+
showSecondaryAction: {
|
|
125
|
+
type: 'boolean',
|
|
126
|
+
title: 'Footer secondary action',
|
|
127
|
+
description: 'Adds a download-style icon button inside the footer trigger with a pulsing accent dot.',
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
export const privateLayoutConfiguratorUiSchema: CustomJsonUiSchema7 = {
|
|
135
|
+
shell: {
|
|
136
|
+
'ui:collapsible': true,
|
|
137
|
+
inset: {
|
|
138
|
+
'ui:widget': 'slider',
|
|
139
|
+
'ui:options': { unit: 'px', step: 2, showInput: false },
|
|
140
|
+
'ui:disabledWhen': { path: 'shell.variant', notEq: 'boxed' },
|
|
141
|
+
},
|
|
142
|
+
radius: {
|
|
143
|
+
'ui:disabledWhen': { path: 'shell.variant', notEq: 'boxed' },
|
|
144
|
+
},
|
|
145
|
+
background: {
|
|
146
|
+
'ui:disabledWhen': { path: 'shell.variant', notEq: 'boxed' },
|
|
147
|
+
},
|
|
148
|
+
border: {
|
|
149
|
+
'ui:widget': 'switch',
|
|
150
|
+
'ui:disabledWhen': { path: 'shell.variant', notEq: 'boxed' },
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
sidebar: {
|
|
154
|
+
'ui:collapsible': true,
|
|
155
|
+
'ui:groups': [
|
|
156
|
+
{
|
|
157
|
+
title: 'Visual',
|
|
158
|
+
fields: ['activeIndicator', 'groupLabelStyle', 'collapsibleGroups', 'showFeatured'],
|
|
159
|
+
defaultOpen: true,
|
|
160
|
+
},
|
|
161
|
+
],
|
|
162
|
+
collapsibleGroups: { 'ui:widget': 'switch' },
|
|
163
|
+
showFeatured: { 'ui:widget': 'switch' },
|
|
164
|
+
},
|
|
165
|
+
header: {
|
|
166
|
+
'ui:collapsible': true,
|
|
167
|
+
showSecondaryAction: { 'ui:widget': 'switch' },
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
export const defaultPrivateLayoutConfiguratorData: PrivateLayoutConfiguratorData = {
|
|
172
|
+
shell: {
|
|
173
|
+
variant: 'boxed',
|
|
174
|
+
inset: 12,
|
|
175
|
+
radius: '2xl',
|
|
176
|
+
background: 'sidebar',
|
|
177
|
+
border: true,
|
|
178
|
+
maxWidth: 'none',
|
|
179
|
+
},
|
|
180
|
+
sidebar: {
|
|
181
|
+
activeIndicator: 'background',
|
|
182
|
+
groupLabelStyle: 'uppercase',
|
|
183
|
+
collapsibleGroups: false,
|
|
184
|
+
showFeatured: false,
|
|
185
|
+
},
|
|
186
|
+
header: {
|
|
187
|
+
userPlan: 'Pro plan',
|
|
188
|
+
showSecondaryAction: false,
|
|
189
|
+
},
|
|
190
|
+
};
|
|
@@ -13,7 +13,7 @@ import { useRouter } from 'next/navigation';
|
|
|
13
13
|
|
|
14
14
|
import { useAuth } from '@djangocfg/api/auth';
|
|
15
15
|
import { Preloader } from '@djangocfg/ui-core/components';
|
|
16
|
-
import { SidebarInset, SidebarProvider } from '@djangocfg/ui-
|
|
16
|
+
import { SidebarInset, SidebarProvider } from '@djangocfg/ui-core/components';
|
|
17
17
|
|
|
18
18
|
import type { AppLayoutPublicChrome } from '../AppLayout/AppLayout';
|
|
19
19
|
import type { LayoutVisualConfig } from '../types';
|
|
@@ -27,6 +27,8 @@ export interface SidebarItem {
|
|
|
27
27
|
href: string;
|
|
28
28
|
icon?: string | LucideIconType;
|
|
29
29
|
badge?: string | number;
|
|
30
|
+
/** Visual style of `badge`: `'count'` (default, neutral pill) or `'pill'` (accent-tinted, e.g. "lite"/"new"). */
|
|
31
|
+
badgeVariant?: 'count' | 'pill';
|
|
30
32
|
/** Collapsed rail: shown in tooltip; defaults to `label`. */
|
|
31
33
|
tooltip?: string;
|
|
32
34
|
}
|
|
@@ -38,6 +40,32 @@ export interface SidebarGroupConfig {
|
|
|
38
40
|
items: SidebarItem[];
|
|
39
41
|
/** If true, group is only shown when it has items (for dynamic groups like extensions) */
|
|
40
42
|
dynamic?: boolean;
|
|
43
|
+
/** Render group as an accordion (Mailersend/Vercel-style). Label becomes a clickable trigger. */
|
|
44
|
+
collapsible?: boolean;
|
|
45
|
+
/** Initial open state for collapsible groups. Auto-expanded if any child is active. Default `false`. */
|
|
46
|
+
defaultOpen?: boolean;
|
|
47
|
+
/** Icon for the group trigger (only when `collapsible`). */
|
|
48
|
+
icon?: string | LucideIconType;
|
|
49
|
+
/**
|
|
50
|
+
* Hide per-item icons inside the group. Defaults to `true` when `collapsible`,
|
|
51
|
+
* `false` otherwise (Mailersend convention: icons live on the trigger, not on children).
|
|
52
|
+
*/
|
|
53
|
+
hideItemIcons?: boolean;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Active-state visual treatment for sidebar nav items. */
|
|
57
|
+
export type SidebarActiveIndicator = 'background' | 'rail' | 'both';
|
|
58
|
+
|
|
59
|
+
/** Rendering of group labels. `'uppercase'` is the legacy ultra-light caps; `'plain'` is sm bold. */
|
|
60
|
+
export type SidebarGroupLabelStyle = 'uppercase' | 'plain';
|
|
61
|
+
|
|
62
|
+
/** Featured CTA tile rendered below groups. */
|
|
63
|
+
export interface SidebarFeaturedConfig {
|
|
64
|
+
icon?: string | LucideIconType;
|
|
65
|
+
label: string;
|
|
66
|
+
href: string;
|
|
67
|
+
badge?: string;
|
|
68
|
+
accent?: 'green' | 'blue' | 'amber' | 'primary';
|
|
41
69
|
}
|
|
42
70
|
|
|
43
71
|
export interface SidebarConfig {
|
|
@@ -65,6 +93,12 @@ export interface SidebarConfig {
|
|
|
65
93
|
menuEndShowOnCollapsed?: boolean;
|
|
66
94
|
/** Custom footer component rendered at the bottom of the sidebar */
|
|
67
95
|
footer?: ReactNode;
|
|
96
|
+
/** Active-state visual on nav items. Default `'background'` (legacy). */
|
|
97
|
+
activeIndicator?: SidebarActiveIndicator;
|
|
98
|
+
/** Style of group labels. Default `'uppercase'` (legacy). Collapsible groups always use `plain`. */
|
|
99
|
+
groupLabelStyle?: SidebarGroupLabelStyle;
|
|
100
|
+
/** Featured CTA tile rendered below all groups, above `menuEnd`. */
|
|
101
|
+
featured?: SidebarFeaturedConfig;
|
|
68
102
|
}
|
|
69
103
|
|
|
70
104
|
export interface HeaderConfig {
|
|
@@ -86,6 +120,17 @@ export interface HeaderConfig {
|
|
|
86
120
|
groups?: UserMenuConfig['groups'];
|
|
87
121
|
/** Auth page path (for sign in button) */
|
|
88
122
|
authPath?: string;
|
|
123
|
+
/** Subtitle under the display name in the sidebar footer (e.g. "Max plan"). */
|
|
124
|
+
userPlan?: string;
|
|
125
|
+
/** Optional secondary action button rendered inside the footer trigger (e.g. Get apps download button). */
|
|
126
|
+
footerSecondaryAction?: {
|
|
127
|
+
icon: string | LucideIconType;
|
|
128
|
+
href?: string;
|
|
129
|
+
onClick?: () => void;
|
|
130
|
+
ariaLabel: string;
|
|
131
|
+
/** Show pulsing accent dot on the action (Claude-style "new"). */
|
|
132
|
+
pulse?: boolean;
|
|
133
|
+
};
|
|
89
134
|
}
|
|
90
135
|
|
|
91
136
|
export interface PrivateLayoutProps {
|