@djangocfg/layouts 2.1.482 → 2.1.483

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@djangocfg/layouts",
3
- "version": "2.1.482",
3
+ "version": "2.1.483",
4
4
  "description": "Simple, straightforward layout components for Next.js - import and use with props",
5
5
  "keywords": [
6
6
  "layouts",
@@ -89,12 +89,12 @@
89
89
  "check": "tsc --noEmit"
90
90
  },
91
91
  "peerDependencies": {
92
- "@djangocfg/analytics": "^2.1.482",
93
- "@djangocfg/api": "^2.1.482",
94
- "@djangocfg/centrifugo": "^2.1.482",
95
- "@djangocfg/devtools": "^2.1.482",
96
- "@djangocfg/i18n": "^2.1.482",
97
- "@djangocfg/ui-core": "^2.1.482",
92
+ "@djangocfg/analytics": "^2.1.483",
93
+ "@djangocfg/api": "^2.1.483",
94
+ "@djangocfg/centrifugo": "^2.1.483",
95
+ "@djangocfg/devtools": "^2.1.483",
96
+ "@djangocfg/i18n": "^2.1.483",
97
+ "@djangocfg/ui-core": "^2.1.483",
98
98
  "@hookform/resolvers": "^5.2.2",
99
99
  "consola": "^3.4.2",
100
100
  "lucide-react": "^0.545.0",
@@ -124,14 +124,14 @@
124
124
  "uuid": "^11.1.1"
125
125
  },
126
126
  "devDependencies": {
127
- "@djangocfg/analytics": "^2.1.482",
128
- "@djangocfg/api": "^2.1.482",
129
- "@djangocfg/centrifugo": "^2.1.482",
130
- "@djangocfg/devtools": "^2.1.482",
131
- "@djangocfg/i18n": "^2.1.482",
132
- "@djangocfg/typescript-config": "^2.1.482",
133
- "@djangocfg/ui-core": "^2.1.482",
134
- "@djangocfg/ui-tools": "^2.1.482",
127
+ "@djangocfg/analytics": "^2.1.483",
128
+ "@djangocfg/api": "^2.1.483",
129
+ "@djangocfg/centrifugo": "^2.1.483",
130
+ "@djangocfg/devtools": "^2.1.483",
131
+ "@djangocfg/i18n": "^2.1.483",
132
+ "@djangocfg/typescript-config": "^2.1.483",
133
+ "@djangocfg/ui-core": "^2.1.483",
134
+ "@djangocfg/ui-tools": "^2.1.483",
135
135
  "@types/node": "^25.9.5",
136
136
  "@types/react": "19.2.15",
137
137
  "@types/react-dom": "19.2.3",
@@ -50,7 +50,7 @@ import { AuthLayout } from '@djangocfg/layouts';
50
50
 
51
51
  <AuthLayout
52
52
  sourceUrl="https://myapp.com"
53
- redirectUrl="/dashboard"
53
+ redirectUrl="/private"
54
54
  enableGithubAuth
55
55
  termsUrl="/terms"
56
56
  privacyUrl="/privacy"
@@ -100,7 +100,7 @@ import { AuthLayout } from '@djangocfg/layouts';
100
100
  | `background` | `AuthBackgroundConfig` | — | Background image/gradient/overlay/blur |
101
101
  | `sidebar` | `ReactNode` | — | Right column content (split variant only) |
102
102
  | `sourceUrl` | `string` | — | App URL for analytics/tracking |
103
- | `redirectUrl` | `string` | `/dashboard` | Where to redirect after auth |
103
+ | `redirectUrl` | `string` | `/private` | Where to redirect after auth |
104
104
  | `enableGithubAuth` | `boolean` | `false` | Show GitHub OAuth button |
105
105
  | `enablePhoneAuth` | `boolean` | `false` | Allow phone number input |
106
106
  | `logo` | `ReactNode` | — | Logo node shown above the form and on the success screen. Fills its slot (the layout sizes it); use a `currentColor` SVG for theme adaptivity |
@@ -48,9 +48,12 @@ function IdentifierStepRaw() {
48
48
  enableGithubAuth,
49
49
  sourceUrl,
50
50
  marketingConsent,
51
+ rememberMe,
51
52
  marketingConsentConfig,
53
+ rememberMeConfig,
52
54
  setIdentifier,
53
55
  setMarketingConsent,
56
+ setRememberMe,
54
57
  setError,
55
58
  handleIdentifierSubmit,
56
59
  } = useAuthFormContext();
@@ -100,6 +103,7 @@ function IdentifierStepRaw() {
100
103
  // GitHub OAuth
101
104
  const { isLoading: isGithubLoading, startGithubAuth } = useGithubAuth({
102
105
  sourceUrl,
106
+ rememberMe,
103
107
  onError: setError,
104
108
  });
105
109
 
@@ -142,6 +146,20 @@ function IdentifierStepRaw() {
142
146
  </label>
143
147
  )}
144
148
 
149
+ {rememberMeConfig?.enabled !== false && (
150
+ <label className="auth-remember">
151
+ <Checkbox
152
+ checked={rememberMe}
153
+ onCheckedChange={(checked) => setRememberMe(checked === true)}
154
+ disabled={isLoading}
155
+ className="auth-remember-checkbox"
156
+ />
157
+ <span className="auth-remember-label">
158
+ {rememberMeConfig?.label ?? 'Keep me signed in for 30 days'}
159
+ </span>
160
+ </label>
161
+ )}
162
+
145
163
  <AuthError message={error} />
146
164
 
147
165
  <AuthButton
@@ -28,6 +28,7 @@ export const AuthFormProvider: React.FC<AuthLayoutProps> = ({
28
28
  logo,
29
29
  redirectUrl,
30
30
  marketingConsent,
31
+ rememberMe,
31
32
  onIdentifierSuccess,
32
33
  onOTPSuccess,
33
34
  onError,
@@ -64,6 +65,7 @@ export const AuthFormProvider: React.FC<AuthLayoutProps> = ({
64
65
  redirectUrl,
65
66
  // Renamed: `marketingConsent` (the name) is the checkbox STATE from the form hook.
66
67
  marketingConsentConfig: marketingConsent,
68
+ rememberMeConfig: rememberMe,
67
69
  }),
68
70
  // eslint-disable-next-line react-hooks/exhaustive-deps
69
71
  [
@@ -76,6 +78,7 @@ export const AuthFormProvider: React.FC<AuthLayoutProps> = ({
76
78
  logo,
77
79
  redirectUrl,
78
80
  marketingConsent,
81
+ rememberMe,
79
82
  ]
80
83
  );
81
84
 
@@ -50,6 +50,14 @@ export interface AuthMarketingConsentConfig {
50
50
  defaultChecked?: boolean;
51
51
  }
52
52
 
53
+ /** Persistent-login checkbox on the identifier step. */
54
+ export interface AuthRememberMeConfig {
55
+ /** Render the checkbox. Default: true. */
56
+ enabled?: boolean;
57
+ /** Checkbox label. Default: "Keep me signed in for 30 days". */
58
+ label?: React.ReactNode;
59
+ }
60
+
53
61
  export interface AuthLayoutConfig {
54
62
  /** Support page URL */
55
63
  supportUrl?: string;
@@ -64,10 +72,12 @@ export interface AuthLayoutConfig {
64
72
  /** Logo node rendered above the form and on the success screen. Pass any
65
73
  * React node — an inline-SVG brand component, an `<img>`, etc. */
66
74
  logo?: React.ReactNode;
67
- /** URL to redirect after successful auth (default: /dashboard) */
75
+ /** URL to redirect after successful auth (default: /private) */
68
76
  redirectUrl?: string;
69
77
  /** Optional marketing-consent checkbox on the identifier step. Absent ⇒ no checkbox, OTP body unchanged. */
70
78
  marketingConsent?: AuthMarketingConsentConfig;
79
+ /** Optional persistent-login control. */
80
+ rememberMe?: AuthRememberMeConfig;
71
81
  }
72
82
 
73
83
  // ─────────────────────────────────────────────────────────────────────────────
@@ -77,8 +87,9 @@ export interface AuthLayoutConfig {
77
87
  // `marketingConsent` is omitted from the config side of the merge: the name is
78
88
  // taken by the checkbox *state* (boolean | null) from AuthFormReturn, so the
79
89
  // prop object is re-exposed as `marketingConsentConfig`.
80
- export interface AuthFormContextType extends AuthFormReturn, Omit<AuthLayoutConfig, 'marketingConsent'> {
90
+ export interface AuthFormContextType extends AuthFormReturn, Omit<AuthLayoutConfig, 'marketingConsent' | 'rememberMe'> {
81
91
  marketingConsentConfig?: AuthMarketingConsentConfig;
92
+ rememberMeConfig?: AuthRememberMeConfig;
82
93
  }
83
94
 
84
95
  // ─────────────────────────────────────────────────────────────────────────────
@@ -140,14 +140,14 @@ export function UserMenu({
140
140
  }
141
141
  if (disableAuth) {
142
142
  return (
143
- <Button variant="default" size="sm" className="rounded-full px-5" disabled>
143
+ <Button variant="default" size="sm" className="h-9 rounded-full px-5" disabled>
144
144
  {labels.signIn}
145
145
  </Button>
146
146
  );
147
147
  }
148
148
  return (
149
149
  <Link href={resolvedAuthPath}>
150
- <Button variant="default" size="sm" className="rounded-full px-5">
150
+ <Button variant="default" size="sm" className="h-9 rounded-full px-5">
151
151
  {labels.signIn}
152
152
  </Button>
153
153
  </Link>
@@ -22,10 +22,11 @@ export interface NavAction {
22
22
  * - `link` — plain text link
23
23
  * - `ghost` — transparent pill, fills on hover (default)
24
24
  * - `outline` — bordered pill
25
+ * - `secondary` — solid neutral pill (a real action, not the main CTA)
25
26
  * - `primary` — solid high-contrast pill (main CTA)
26
27
  * @default 'ghost'
27
28
  */
28
- variant?: 'link' | 'ghost' | 'outline' | 'primary';
29
+ variant?: 'link' | 'ghost' | 'outline' | 'secondary' | 'primary';
29
30
  /** Hide on screens below lg (useful for secondary CTAs). @default false */
30
31
  hideOnSmall?: boolean;
31
32
  /** Optional inline icon (use lucide-react or any ReactNode). */
@@ -37,15 +38,27 @@ export interface NavAction {
37
38
  const baseCls =
38
39
  'inline-flex shrink-0 items-center justify-center gap-1.5 rounded-full text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/35';
39
40
 
41
+ // Height is FIXED (`h-9`), not a `min-h-9` floor. With a minimum the pill grew
42
+ // past its neighbours as soon as it carried an icon — the navbar's own UserMenu
43
+ // login button is a ui-core `Button size="sm"` (a fixed `h-8`), so a growing CTA
44
+ // sat visibly taller than the control right next to it. A nav action is a
45
+ // control in a row of controls: its height must be a constant, and the label is
46
+ // already truncated rather than wrapped.
40
47
  const variantCls: Record<NonNullable<NavAction['variant']>, string> = {
41
48
  link:
42
49
  'px-1.5 py-1 text-foreground/85 hover:text-foreground',
43
50
  ghost:
44
- 'px-4 py-1.5 min-h-9 text-foreground/90 hover:bg-accent/55 hover:text-foreground',
51
+ 'h-9 px-4 text-foreground/90 hover:bg-accent/55 hover:text-foreground',
45
52
  outline:
46
- 'px-4 py-1.5 min-h-9 border border-border/70 text-foreground/90 hover:bg-accent/40 hover:text-foreground',
53
+ 'h-9 px-4 border border-border/70 text-foreground/90 hover:bg-accent/40 hover:text-foreground',
54
+ // Filled but neutral — the middle rung between `outline` (which disappears
55
+ // into a dark navbar) and `primary` (which competes with the one real CTA).
56
+ // Uses the `secondary` token pair rather than a hand-mixed tint so it retunes
57
+ // with the active preset.
58
+ secondary:
59
+ 'h-9 px-4 bg-secondary text-secondary-foreground hover:bg-secondary/80 shadow-sm',
47
60
  primary:
48
- 'px-4 py-1.5 min-h-9 bg-primary text-primary-foreground hover:bg-primary/90 shadow-sm',
61
+ 'h-9 px-4 bg-primary text-primary-foreground hover:bg-primary/90 shadow-sm',
49
62
  };
50
63
 
51
64
  interface NavActionItemProps {