@marianmeres/stuic 3.72.1 → 3.73.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.
@@ -7,7 +7,8 @@
7
7
  :root {
8
8
  /* Default look is a pill switcher. Override radius/bg to get a square group. */
9
9
  --stuic-button-group-radius: 9999px;
10
- --stuic-button-group-padding: 3px;
10
+ --stuic-button-group-padding-x: 4px;
11
+ --stuic-button-group-padding-y: 3px;
11
12
  --stuic-button-group-gap: 0.25rem;
12
13
 
13
14
  /* Button sizing — overall height = min-height + 2×padding + 2×border = 44px (Apple HIG min) */
@@ -53,7 +54,7 @@
53
54
  width: 100%;
54
55
 
55
56
  /* Box model */
56
- padding: var(--stuic-button-group-padding);
57
+ padding: var(--stuic-button-group-padding-y) var(--stuic-button-group-padding-x);
57
58
  border-width: var(--stuic-button-group-border-width, var(--stuic-border-width));
58
59
  border-style: solid;
59
60
  border-radius: var(--stuic-button-group-radius, var(--stuic-radius-button));
@@ -73,8 +73,6 @@
73
73
  unstyled?: boolean;
74
74
  class?: string;
75
75
  el?: HTMLFormElement;
76
-
77
- compact?: boolean;
78
76
  }
79
77
  </script>
80
78
 
@@ -111,7 +109,6 @@
111
109
  unstyled = false,
112
110
  class: classProp,
113
111
  el = $bindable(),
114
- compact,
115
112
  ...rest
116
113
  }: Props = $props();
117
114
 
@@ -158,13 +155,7 @@
158
155
  let _class = $derived(unstyled ? classProp : twMerge("stuic-login-form", classProp));
159
156
  </script>
160
157
 
161
- <form
162
- bind:this={el}
163
- class={_class}
164
- use:onSubmitValidityCheck
165
- {...rest}
166
- data-compact={compact ? "" : undefined}
167
- >
158
+ <form bind:this={el} class={_class} use:onSubmitValidityCheck {...rest}>
168
159
  <!-- General error alert -->
169
160
  <DismissibleMessage message={error} intent="destructive" onDismiss={false} />
170
161
 
@@ -184,7 +175,6 @@
184
175
  required
185
176
  name="login-email"
186
177
  labelLeftBreakpoint={0}
187
- class={compact ? "mb-4" : ""}
188
178
  validate={{
189
179
  customValidator(val) {
190
180
  return fieldError("email") || "";
@@ -203,7 +193,6 @@
203
193
  required
204
194
  name="login-password"
205
195
  labelLeftBreakpoint={0}
206
- class={compact ? "mb-4" : ""}
207
196
  validate={{
208
197
  customValidator(val) {
209
198
  return fieldError("password") || "";
@@ -211,9 +200,22 @@
211
200
  }}
212
201
  />
213
202
 
214
- {#if compact}
215
- <!-- Compact: remember me + submit in one row -->
216
- <div class={unstyled ? undefined : "stuic-login-form-compact-cta"}>
203
+ <!-- CTA -->
204
+ {#if submitButton}
205
+ {@render submitButton({ isSubmitting, disabled: isSubmitting })}
206
+ {:else}
207
+ <div class={unstyled ? undefined : "stuic-login-form-submit"}>
208
+ <Button intent="primary" type="submit" disabled={isSubmitting} class="w-full">
209
+ {isSubmitting
210
+ ? (submittingLabel ?? t("login_form.submitting"))
211
+ : (submitLabel ?? t("login_form.submit"))}
212
+ </Button>
213
+ </div>
214
+ {/if}
215
+
216
+ <!-- Remember me + Forgot password -->
217
+ {#if showRememberMe || onForgotPassword}
218
+ <div class={unstyled ? undefined : "stuic-login-form-options"}>
217
219
  {#if showRememberMe}
218
220
  <!-- svelte-ignore binding_property_non_reactive -->
219
221
  <span use:tooltip aria-label={t("login_form.remember_me_tooltip")}>
@@ -225,58 +227,18 @@
225
227
  />
226
228
  </span>
227
229
  {/if}
228
- {#if submitButton}
229
- {@render submitButton({ isSubmitting, disabled: isSubmitting })}
230
- {:else}
231
- <Button intent="primary" type="submit" disabled={isSubmitting} class="block">
232
- {isSubmitting
233
- ? (submittingLabel ?? t("login_form.submitting"))
234
- : (submitLabel ?? t("login_form.submit"))}
230
+ {#if onForgotPassword}
231
+ <Button
232
+ variant="link"
233
+ type="button"
234
+ class={unstyled ? undefined : "text-muted-foreground ml-auto"}
235
+ size="sm"
236
+ onclick={onForgotPassword}
237
+ >
238
+ {t("login_form.forgot_password")}
235
239
  </Button>
236
240
  {/if}
237
241
  </div>
238
- {:else}
239
- <!-- Normal: remember me above submit -->
240
- {#if showRememberMe}
241
- <!-- svelte-ignore binding_property_non_reactive -->
242
- <div class={unstyled ? undefined : "stuic-login-form-remember"}>
243
- <span use:tooltip aria-label={t("login_form.remember_me_tooltip")}>
244
- <FieldCheckbox
245
- bind:checked={formData.rememberMe}
246
- label={t("login_form.remember_me")}
247
- name="login-remember-me"
248
- />
249
- </span>
250
- </div>
251
- {/if}
252
-
253
- <!-- CTA -->
254
- {#if submitButton}
255
- {@render submitButton({ isSubmitting, disabled: isSubmitting })}
256
- {:else}
257
- <div class={unstyled ? undefined : "stuic-login-form-submit"}>
258
- <Button intent="primary" type="submit" disabled={isSubmitting} class="w-full">
259
- {isSubmitting
260
- ? (submittingLabel ?? t("login_form.submitting"))
261
- : (submitLabel ?? t("login_form.submit"))}
262
- </Button>
263
- </div>
264
- {/if}
265
- {/if}
266
-
267
- <!-- Forgot password -->
268
- {#if onForgotPassword}
269
- <div class={unstyled ? undefined : "stuic-login-form-forgot"}>
270
- <Button
271
- variant="link"
272
- type="button"
273
- class="text-muted-foreground"
274
- size="sm"
275
- onclick={onForgotPassword}
276
- >
277
- {t("login_form.forgot_password")}
278
- </Button>
279
- </div>
280
242
  {/if}
281
243
 
282
244
  <!-- Social logins -->
@@ -57,7 +57,6 @@ export interface Props extends Omit<HTMLAttributes<HTMLFormElement>, "children">
57
57
  unstyled?: boolean;
58
58
  class?: string;
59
59
  el?: HTMLFormElement;
60
- compact?: boolean;
61
60
  }
62
61
  declare const LoginForm: import("svelte").Component<Props, {}, "el" | "formData">;
63
62
  type LoginForm = ReturnType<typeof LoginForm>;
@@ -154,7 +154,7 @@
154
154
  classDialog="flex items-center justify-center"
155
155
  >
156
156
  {#snippet header()}
157
- <div class="flex items-center justify-between p-4 pb-0">
157
+ <div class="flex items-center justify-between p-4">
158
158
  <H level={1} renderLevel={3} class="pl-2">
159
159
  {title ?? t("login_form.modal_title")}
160
160
  </H>
@@ -193,7 +193,6 @@
193
193
  t={tProp}
194
194
  {unstyled}
195
195
  class={classForm}
196
- compact
197
196
  />
198
197
  </div>
199
198
  </Modal>
@@ -2,11 +2,9 @@
2
2
  /* LoginForm */
3
3
  --stuic-login-form-gap: 0rem;
4
4
  --stuic-login-form-gap-row: 1rem;
5
- --stuic-login-form-forgot-margin-y: 0.5rem;
6
- --stuic-login-form-forgot-margin-x: 0.5rem;
7
5
 
8
6
  /* Social login section */
9
- --stuic-login-form-social-margin-top: 1rem;
7
+ --stuic-login-form-social-margin-top: 1.5rem;
10
8
  --stuic-login-form-social-gap: 0.75rem;
11
9
  --stuic-login-form-social-divider-color: var(--stuic-color-muted-foreground);
12
10
  --stuic-login-form-social-divider-line-color: var(--stuic-color-border);
@@ -21,18 +19,15 @@
21
19
  gap: var(--stuic-login-form-gap);
22
20
  }
23
21
 
24
- .stuic-login-form-forgot {
25
- margin: var(--stuic-login-form-forgot-margin-y)
26
- var(--stuic-login-form-forgot-margin-x) var(--stuic-login-form-forgot-margin-y)
27
- var(--stuic-login-form-forgot-margin-x);
28
- }
29
-
30
- .stuic-login-form-remember {
31
- margin-top: 0.25rem;
22
+ .stuic-login-form-submit {
23
+ margin-top: 1.5rem;
24
+ margin-bottom: 1.5rem;
32
25
  }
33
26
 
34
- .stuic-login-form-submit {
35
- margin-top: 1rem;
27
+ .stuic-login-form-options {
28
+ display: flex;
29
+ align-items: center;
30
+ gap: 1rem;
36
31
  }
37
32
 
38
33
  /* Social login container */
@@ -63,21 +58,9 @@
63
58
  flex-direction: column;
64
59
  gap: var(--stuic-login-form-social-gap);
65
60
  }
61
+ }
66
62
 
67
- .stuic-login-form-compact-cta {
68
- display: flex;
69
- align-items: center;
70
- /* justify-content: space-between; */
71
- margin-top: 0.5rem;
72
- gap: 2rem;
73
- }
74
-
75
- .stuic-login-form[data-compact] {
76
- .stuic-login-form-submit {
77
- margin-top: 0.5rem;
78
- }
79
- .stuic-button {
80
- flex: 1;
81
- }
82
- }
63
+ /* Tighten default FieldInput bottom margin (mb-8 → 1rem) inside the form */
64
+ .stuic-login-form .stuic-input {
65
+ margin-bottom: 1rem;
83
66
  }
@@ -40,8 +40,14 @@
40
40
  /** Applied to both inner forms' submit buttons. */
41
41
  isSubmitting?: boolean;
42
42
 
43
+ /**
44
+ * Called when "Forgot password?" is clicked in login mode.
45
+ * If undefined, the link is not rendered.
46
+ */
47
+ onForgotPassword?: () => void;
48
+
43
49
  /** Pass-through props for the inner LoginForm (spread). */
44
- loginProps?: Omit<LoginFormProps, InnerPropsCommonOmit>;
50
+ loginProps?: Omit<LoginFormProps, InnerPropsCommonOmit | "onForgotPassword">;
45
51
 
46
52
  /** Pass-through props for the inner RegisterForm (spread). */
47
53
  registerProps?: Omit<RegisterFormProps, InnerPropsCommonOmit>;
@@ -137,6 +143,7 @@
137
143
  onVerify,
138
144
  onResendCode,
139
145
  isSubmitting = false,
146
+ onForgotPassword,
140
147
  loginProps,
141
148
  registerProps,
142
149
  verifyProps,
@@ -221,6 +228,7 @@
221
228
  onSubmit={onLogin}
222
229
  {isSubmitting}
223
230
  {notifications}
231
+ {onForgotPassword}
224
232
  t={tProp}
225
233
  {...loginProps}
226
234
  />
@@ -22,8 +22,13 @@ export interface Props extends Omit<HTMLAttributes<HTMLDivElement>, "children">
22
22
  onRegister: (data: RegisterFormData) => void;
23
23
  /** Applied to both inner forms' submit buttons. */
24
24
  isSubmitting?: boolean;
25
+ /**
26
+ * Called when "Forgot password?" is clicked in login mode.
27
+ * If undefined, the link is not rendered.
28
+ */
29
+ onForgotPassword?: () => void;
25
30
  /** Pass-through props for the inner LoginForm (spread). */
26
- loginProps?: Omit<LoginFormProps, InnerPropsCommonOmit>;
31
+ loginProps?: Omit<LoginFormProps, InnerPropsCommonOmit | "onForgotPassword">;
27
32
  /** Pass-through props for the inner RegisterForm (spread). */
28
33
  registerProps?: Omit<RegisterFormProps, InnerPropsCommonOmit>;
29
34
  /**
@@ -33,6 +33,12 @@
33
33
 
34
34
  isSubmitting?: boolean;
35
35
 
36
+ /**
37
+ * Called when "Forgot password?" is clicked in login mode.
38
+ * If undefined, the link is not rendered.
39
+ */
40
+ onForgotPassword?: () => void;
41
+
36
42
  loginProps?: InnerProps["loginProps"];
37
43
  registerProps?: InnerProps["registerProps"];
38
44
  verifyProps?: InnerProps["verifyProps"];
@@ -101,6 +107,7 @@
101
107
  onVerify,
102
108
  onResendCode,
103
109
  isSubmitting = false,
110
+ onForgotPassword,
104
111
  loginProps,
105
112
  registerProps,
106
113
  verifyProps,
@@ -158,7 +165,7 @@
158
165
  classDialog="flex items-center justify-center"
159
166
  >
160
167
  {#snippet header()}
161
- <div class="flex items-center justify-between p-4 pb-0">
168
+ <div class="flex items-center justify-between p-4">
162
169
  <H level={1} renderLevel={3} class="pl-2">
163
170
  {resolvedTitle}
164
171
  </H>
@@ -188,6 +195,7 @@
188
195
  {onVerify}
189
196
  {onResendCode}
190
197
  {isSubmitting}
198
+ {onForgotPassword}
191
199
  {loginProps}
192
200
  {registerProps}
193
201
  {verifyProps}
@@ -20,6 +20,11 @@ export interface Props {
20
20
  /** Called when the user clicks "Resend code" in the verify view. */
21
21
  onResendCode?: () => Promise<void> | void;
22
22
  isSubmitting?: boolean;
23
+ /**
24
+ * Called when "Forgot password?" is clicked in login mode.
25
+ * If undefined, the link is not rendered.
26
+ */
27
+ onForgotPassword?: () => void;
23
28
  loginProps?: InnerProps["loginProps"];
24
29
  registerProps?: InnerProps["registerProps"];
25
30
  verifyProps?: InnerProps["verifyProps"];
@@ -1,10 +1,10 @@
1
1
  :root {
2
2
  /* LoginOrRegisterForm */
3
- --stuic-login-or-register-form-gap: 1rem;
4
- --stuic-login-or-register-form-switcher-margin-bottom: 1rem;
3
+ --stuic-login-or-register-form-gap: 0rem;
4
+ --stuic-login-or-register-form-switcher-margin-bottom: 2rem;
5
5
 
6
6
  /* Social login section (shared, rendered at composite level) */
7
- --stuic-login-or-register-form-social-margin-top: 1rem;
7
+ --stuic-login-or-register-form-social-margin-top: 1.5rem;
8
8
  --stuic-login-or-register-form-social-gap: 0.75rem;
9
9
  --stuic-login-or-register-form-social-divider-color: var(
10
10
  --stuic-color-muted-foreground
@@ -91,8 +91,6 @@
91
91
  unstyled?: boolean;
92
92
  class?: string;
93
93
  el?: HTMLFormElement;
94
-
95
- compact?: boolean;
96
94
  }
97
95
  </script>
98
96
 
@@ -129,7 +127,6 @@
129
127
  unstyled = false,
130
128
  class: classProp,
131
129
  el = $bindable(),
132
- compact,
133
130
  ...rest
134
131
  }: Props = $props();
135
132
 
@@ -194,13 +191,7 @@
194
191
  let _class = $derived(unstyled ? classProp : twMerge("stuic-register-form", classProp));
195
192
  </script>
196
193
 
197
- <form
198
- bind:this={el}
199
- class={_class}
200
- use:onSubmitValidityCheck
201
- {...rest}
202
- data-compact={compact ? "" : undefined}
203
- >
194
+ <form bind:this={el} class={_class} use:onSubmitValidityCheck {...rest}>
204
195
  <!-- General error alert -->
205
196
  <DismissibleMessage message={error} intent="destructive" onDismiss={false} />
206
197
 
@@ -217,7 +208,6 @@
217
208
  required={cfg.required}
218
209
  name={`register-extra-${cfg.name}`}
219
210
  labelLeftBreakpoint={0}
220
- class={compact ? "mb-4" : ""}
221
211
  validate={{
222
212
  customValidator() {
223
213
  return fieldError(cfg.name) || "";
@@ -243,7 +233,6 @@
243
233
  required
244
234
  name="register-email"
245
235
  labelLeftBreakpoint={0}
246
- class={compact ? "mb-4" : ""}
247
236
  validate={{
248
237
  customValidator() {
249
238
  return fieldError("email") || "";
@@ -263,7 +252,6 @@
263
252
  minlength={passwordMinLength}
264
253
  name="register-password"
265
254
  labelLeftBreakpoint={0}
266
- class={compact ? "mb-4" : ""}
267
255
  validate={{
268
256
  customValidator() {
269
257
  return fieldError("password") || "";
@@ -284,7 +272,6 @@
284
272
  minlength={passwordMinLength}
285
273
  name="register-password-confirm"
286
274
  labelLeftBreakpoint={0}
287
- class={compact ? "mb-4" : ""}
288
275
  validate={{
289
276
  customValidator() {
290
277
  return fieldError("passwordConfirm") || "";
@@ -306,7 +293,6 @@
306
293
  required={cfg.required}
307
294
  name={`register-extra-${cfg.name}`}
308
295
  labelLeftBreakpoint={0}
309
- class={compact ? "mb-4" : ""}
310
296
  validate={{
311
297
  customValidator() {
312
298
  return fieldError(cfg.name) || "";
@@ -324,14 +310,6 @@
324
310
  <!-- CTA -->
325
311
  {#if submitButton}
326
312
  {@render submitButton({ isSubmitting, disabled: isSubmitting })}
327
- {:else if compact}
328
- <div class={unstyled ? undefined : "stuic-register-form-compact-cta"}>
329
- <Button intent="primary" type="submit" disabled={isSubmitting} class="block">
330
- {isSubmitting
331
- ? (submittingLabel ?? t("register_form.submitting"))
332
- : (submitLabel ?? t("register_form.submit"))}
333
- </Button>
334
- </div>
335
313
  {:else}
336
314
  <div class={unstyled ? undefined : "stuic-register-form-submit"}>
337
315
  <Button intent="primary" type="submit" disabled={isSubmitting} class="w-full">
@@ -70,7 +70,6 @@ export interface Props extends Omit<HTMLAttributes<HTMLFormElement>, "children">
70
70
  unstyled?: boolean;
71
71
  class?: string;
72
72
  el?: HTMLFormElement;
73
- compact?: boolean;
74
73
  }
75
74
  declare const RegisterForm: import("svelte").Component<Props, {}, "el" | "formData">;
76
75
  type RegisterForm = ReturnType<typeof RegisterForm>;
@@ -165,7 +165,7 @@
165
165
  classDialog="flex items-center justify-center"
166
166
  >
167
167
  {#snippet header()}
168
- <div class="flex items-center justify-between p-4 pb-0">
168
+ <div class="flex items-center justify-between p-4">
169
169
  <H level={1} renderLevel={3} class="pl-2">
170
170
  {title ?? t("register_form.modal_title")}
171
171
  </H>
@@ -206,7 +206,6 @@
206
206
  t={tProp}
207
207
  {unstyled}
208
208
  class={classForm}
209
- compact
210
209
  />
211
210
  </div>
212
211
  </Modal>
@@ -20,7 +20,8 @@
20
20
  }
21
21
 
22
22
  .stuic-register-form-submit {
23
- margin-top: 1rem;
23
+ margin-top: 1.5rem;
24
+ margin-bottom: 1.5rem;
24
25
  }
25
26
 
26
27
  /* Social login container */
@@ -51,20 +52,9 @@
51
52
  flex-direction: column;
52
53
  gap: var(--stuic-register-form-social-gap);
53
54
  }
55
+ }
54
56
 
55
- .stuic-register-form-compact-cta {
56
- display: flex;
57
- align-items: center;
58
- margin-top: 0.5rem;
59
- gap: 2rem;
60
- }
61
-
62
- .stuic-register-form[data-compact] {
63
- .stuic-register-form-submit {
64
- margin-top: 0.5rem;
65
- }
66
- .stuic-button {
67
- flex: 1;
68
- }
69
- }
57
+ /* Tighten default FieldInput bottom margin (mb-8 → 1rem) inside the form */
58
+ .stuic-register-form .stuic-input {
59
+ margin-bottom: 1rem;
70
60
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marianmeres/stuic",
3
- "version": "3.72.1",
3
+ "version": "3.73.0",
4
4
  "files": [
5
5
  "dist",
6
6
  "!dist/**/*.test.*",