@marianmeres/stuic 3.67.0 → 3.69.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.
Files changed (30) hide show
  1. package/dist/components/DataTable/DataTable.svelte +182 -26
  2. package/dist/components/DataTable/DataTable.svelte.d.ts +45 -3
  3. package/dist/components/DataTable/README.md +84 -8
  4. package/dist/components/DataTable/index.css +24 -0
  5. package/dist/components/LoginOrRegisterForm/LoginOrRegisterForm.svelte +223 -0
  6. package/dist/components/LoginOrRegisterForm/LoginOrRegisterForm.svelte.d.ts +68 -0
  7. package/dist/components/LoginOrRegisterForm/LoginOrRegisterFormModal.svelte +184 -0
  8. package/dist/components/LoginOrRegisterForm/LoginOrRegisterFormModal.svelte.d.ts +55 -0
  9. package/dist/components/LoginOrRegisterForm/_internal/login-or-register-form-i18n-defaults.d.ts +1 -0
  10. package/dist/components/LoginOrRegisterForm/_internal/login-or-register-form-i18n-defaults.js +17 -0
  11. package/dist/components/LoginOrRegisterForm/index.css +66 -0
  12. package/dist/components/LoginOrRegisterForm/index.d.ts +2 -0
  13. package/dist/components/LoginOrRegisterForm/index.js +2 -0
  14. package/dist/components/RegisterForm/RegisterForm.svelte +367 -0
  15. package/dist/components/RegisterForm/RegisterForm.svelte.d.ts +77 -0
  16. package/dist/components/RegisterForm/RegisterFormModal.svelte +212 -0
  17. package/dist/components/RegisterForm/RegisterFormModal.svelte.d.ts +82 -0
  18. package/dist/components/RegisterForm/_internal/register-form-i18n-defaults.d.ts +1 -0
  19. package/dist/components/RegisterForm/_internal/register-form-i18n-defaults.js +30 -0
  20. package/dist/components/RegisterForm/_internal/register-form-types.d.ts +35 -0
  21. package/dist/components/RegisterForm/_internal/register-form-types.js +1 -0
  22. package/dist/components/RegisterForm/_internal/register-form-utils.d.ts +7 -0
  23. package/dist/components/RegisterForm/_internal/register-form-utils.js +55 -0
  24. package/dist/components/RegisterForm/index.css +70 -0
  25. package/dist/components/RegisterForm/index.d.ts +4 -0
  26. package/dist/components/RegisterForm/index.js +3 -0
  27. package/dist/index.css +2 -0
  28. package/dist/index.d.ts +2 -0
  29. package/dist/index.js +2 -0
  30. package/package.json +1 -1
@@ -0,0 +1,223 @@
1
+ <script lang="ts" module>
2
+ import type { Snippet } from "svelte";
3
+ import type { HTMLAttributes } from "svelte/elements";
4
+ import type { TranslateFn } from "../../types.js";
5
+ import type { Props as LoginFormProps } from "../LoginForm/LoginForm.svelte";
6
+ import type { LoginFormData } from "../LoginForm/_internal/login-form-types.js";
7
+ import type { Props as RegisterFormProps } from "../RegisterForm/RegisterForm.svelte";
8
+ import type { RegisterFormData } from "../RegisterForm/_internal/register-form-types.js";
9
+ import type { NotificationsStack } from "../Notifications/notifications-stack.svelte.js";
10
+
11
+ export type LoginOrRegisterFormMode = "login" | "register";
12
+
13
+ type InnerPropsCommonOmit =
14
+ | "formData"
15
+ | "onSubmit"
16
+ | "isSubmitting"
17
+ | "t"
18
+ | "notifications"
19
+ | "socialLogins"
20
+ | "socialDividerLabel"
21
+ | "footer";
22
+
23
+ export interface Props extends Omit<HTMLAttributes<HTMLDivElement>, "children"> {
24
+ /** Bindable active mode. Default: "login" */
25
+ mode?: LoginOrRegisterFormMode;
26
+
27
+ /** Bindable login formData (forwarded to LoginForm) */
28
+ loginData?: LoginFormData;
29
+
30
+ /** Bindable register formData (forwarded to RegisterForm) */
31
+ registerData?: RegisterFormData;
32
+
33
+ /** Called when the inner LoginForm submits successfully. */
34
+ onLogin: (data: LoginFormData) => void;
35
+
36
+ /** Called when the inner RegisterForm submits successfully. */
37
+ onRegister: (data: RegisterFormData) => void;
38
+
39
+ /** Applied to both inner forms' submit buttons. */
40
+ isSubmitting?: boolean;
41
+
42
+ /** Pass-through props for the inner LoginForm (spread). */
43
+ loginProps?: Omit<LoginFormProps, InnerPropsCommonOmit>;
44
+
45
+ /** Pass-through props for the inner RegisterForm (spread). */
46
+ registerProps?: Omit<RegisterFormProps, InnerPropsCommonOmit>;
47
+
48
+ /** Override the built-in ButtonGroupRadio mode switcher. */
49
+ modeSwitcher?: Snippet<
50
+ [
51
+ {
52
+ mode: LoginOrRegisterFormMode;
53
+ setMode: (m: LoginOrRegisterFormMode) => void;
54
+ t: TranslateFn;
55
+ },
56
+ ]
57
+ >;
58
+
59
+ /** Override the "Log in" tab label. */
60
+ loginModeLabel?: string;
61
+
62
+ /** Override the "Sign up" tab label. */
63
+ registerModeLabel?: string;
64
+
65
+ /**
66
+ * Social/OAuth login buttons rendered ONCE below the active form.
67
+ * Shared between both modes (OAuth works for either flow).
68
+ */
69
+ socialLogins?: Snippet;
70
+
71
+ /**
72
+ * Override the divider label above social login buttons.
73
+ * Default: i18n key "login_or_register_form.social_divider".
74
+ * Set to `false` to hide the divider while still rendering socialLogins.
75
+ */
76
+ socialDividerLabel?: string | false;
77
+
78
+ /**
79
+ * Footer snippet. Receives the current mode + setter so consumers can
80
+ * render mode-aware content (e.g., "Already have an account? Sign in").
81
+ */
82
+ footer?: Snippet<
83
+ [
84
+ {
85
+ mode: LoginOrRegisterFormMode;
86
+ setMode: (m: LoginOrRegisterFormMode) => void;
87
+ },
88
+ ]
89
+ >;
90
+
91
+ notifications?: NotificationsStack;
92
+ t?: TranslateFn;
93
+ unstyled?: boolean;
94
+ class?: string;
95
+ }
96
+ </script>
97
+
98
+ <script lang="ts">
99
+ import { twMerge } from "../../utils/tw-merge.js";
100
+ import { t_default } from "./_internal/login-or-register-form-i18n-defaults.js";
101
+ import LoginForm from "../LoginForm/LoginForm.svelte";
102
+ import { createEmptyLoginFormData } from "../LoginForm/_internal/login-form-utils.js";
103
+ import RegisterForm from "../RegisterForm/RegisterForm.svelte";
104
+ import { createEmptyRegisterFormData } from "../RegisterForm/_internal/register-form-utils.js";
105
+ import ButtonGroupRadio from "../ButtonGroupRadio/ButtonGroupRadio.svelte";
106
+
107
+ let {
108
+ mode = $bindable("login"),
109
+ loginData = $bindable(createEmptyLoginFormData()),
110
+ registerData = $bindable(createEmptyRegisterFormData()),
111
+ onLogin,
112
+ onRegister,
113
+ isSubmitting = false,
114
+ loginProps,
115
+ registerProps,
116
+ modeSwitcher,
117
+ loginModeLabel,
118
+ registerModeLabel,
119
+ socialLogins,
120
+ socialDividerLabel,
121
+ footer,
122
+ notifications,
123
+ t: tProp,
124
+ unstyled = false,
125
+ class: classProp,
126
+ ...rest
127
+ }: Props = $props();
128
+
129
+ let t = $derived(tProp ?? t_default);
130
+
131
+ let loginLabel = $derived(loginModeLabel ?? t("login_or_register_form.mode_login"));
132
+ let registerLabel = $derived(
133
+ registerModeLabel ?? t("login_or_register_form.mode_register")
134
+ );
135
+
136
+ // One-shot email transfer at the transition boundary. Keeps the email
137
+ // persistent across mode switches without setting up a continuous sync
138
+ // effect (which would be prone to loops).
139
+ function setMode(next: LoginOrRegisterFormMode) {
140
+ if (next === mode) return;
141
+ if (next === "register") {
142
+ registerData.email = loginData.email;
143
+ } else {
144
+ loginData.email = registerData.email;
145
+ }
146
+ mode = next;
147
+ }
148
+
149
+ let switcherOptions = $derived([
150
+ { label: loginLabel, value: "login" },
151
+ { label: registerLabel, value: "register" },
152
+ ]);
153
+
154
+ let _class = $derived(
155
+ unstyled ? classProp : twMerge("stuic-login-or-register-form", classProp)
156
+ );
157
+ </script>
158
+
159
+ <div class={_class} {...rest}>
160
+ <!-- Mode switcher -->
161
+ <div class={unstyled ? undefined : "stuic-login-or-register-form-switcher"}>
162
+ {#if modeSwitcher}
163
+ {@render modeSwitcher({ mode, setMode, t })}
164
+ {:else}
165
+ <ButtonGroupRadio
166
+ options={switcherOptions}
167
+ value={mode}
168
+ onButtonClick={(idx) => {
169
+ setMode(idx === 0 ? "login" : "register");
170
+ return false;
171
+ }}
172
+ />
173
+ {/if}
174
+ </div>
175
+
176
+ <!-- Active form -->
177
+ <div class={unstyled ? undefined : "stuic-login-or-register-form-body"}>
178
+ {#if mode === "login"}
179
+ <!-- svelte-ignore binding_property_non_reactive -->
180
+ <LoginForm
181
+ bind:formData={loginData}
182
+ onSubmit={onLogin}
183
+ {isSubmitting}
184
+ {notifications}
185
+ t={tProp}
186
+ {...loginProps}
187
+ />
188
+ {:else}
189
+ <!-- svelte-ignore binding_property_non_reactive -->
190
+ <RegisterForm
191
+ bind:formData={registerData}
192
+ onSubmit={onRegister}
193
+ {isSubmitting}
194
+ {notifications}
195
+ t={tProp}
196
+ {...registerProps}
197
+ />
198
+ {/if}
199
+ </div>
200
+
201
+ <!-- Shared social logins -->
202
+ {#if socialLogins}
203
+ <div class={unstyled ? undefined : "stuic-login-or-register-form-social"}>
204
+ {#if socialDividerLabel !== false}
205
+ <div class={unstyled ? undefined : "stuic-login-or-register-form-social-divider"}>
206
+ <span>
207
+ {typeof socialDividerLabel === "string"
208
+ ? socialDividerLabel
209
+ : t("login_or_register_form.social_divider")}
210
+ </span>
211
+ </div>
212
+ {/if}
213
+ <div class={unstyled ? undefined : "stuic-login-or-register-form-social-buttons"}>
214
+ {@render socialLogins()}
215
+ </div>
216
+ </div>
217
+ {/if}
218
+
219
+ <!-- Footer -->
220
+ {#if footer}
221
+ {@render footer({ mode, setMode })}
222
+ {/if}
223
+ </div>
@@ -0,0 +1,68 @@
1
+ import type { Snippet } from "svelte";
2
+ import type { HTMLAttributes } from "svelte/elements";
3
+ import type { TranslateFn } from "../../types.js";
4
+ import type { Props as LoginFormProps } from "../LoginForm/LoginForm.svelte";
5
+ import type { LoginFormData } from "../LoginForm/_internal/login-form-types.js";
6
+ import type { Props as RegisterFormProps } from "../RegisterForm/RegisterForm.svelte";
7
+ import type { RegisterFormData } from "../RegisterForm/_internal/register-form-types.js";
8
+ import type { NotificationsStack } from "../Notifications/notifications-stack.svelte.js";
9
+ export type LoginOrRegisterFormMode = "login" | "register";
10
+ type InnerPropsCommonOmit = "formData" | "onSubmit" | "isSubmitting" | "t" | "notifications" | "socialLogins" | "socialDividerLabel" | "footer";
11
+ export interface Props extends Omit<HTMLAttributes<HTMLDivElement>, "children"> {
12
+ /** Bindable active mode. Default: "login" */
13
+ mode?: LoginOrRegisterFormMode;
14
+ /** Bindable login formData (forwarded to LoginForm) */
15
+ loginData?: LoginFormData;
16
+ /** Bindable register formData (forwarded to RegisterForm) */
17
+ registerData?: RegisterFormData;
18
+ /** Called when the inner LoginForm submits successfully. */
19
+ onLogin: (data: LoginFormData) => void;
20
+ /** Called when the inner RegisterForm submits successfully. */
21
+ onRegister: (data: RegisterFormData) => void;
22
+ /** Applied to both inner forms' submit buttons. */
23
+ isSubmitting?: boolean;
24
+ /** Pass-through props for the inner LoginForm (spread). */
25
+ loginProps?: Omit<LoginFormProps, InnerPropsCommonOmit>;
26
+ /** Pass-through props for the inner RegisterForm (spread). */
27
+ registerProps?: Omit<RegisterFormProps, InnerPropsCommonOmit>;
28
+ /** Override the built-in ButtonGroupRadio mode switcher. */
29
+ modeSwitcher?: Snippet<[
30
+ {
31
+ mode: LoginOrRegisterFormMode;
32
+ setMode: (m: LoginOrRegisterFormMode) => void;
33
+ t: TranslateFn;
34
+ }
35
+ ]>;
36
+ /** Override the "Log in" tab label. */
37
+ loginModeLabel?: string;
38
+ /** Override the "Sign up" tab label. */
39
+ registerModeLabel?: string;
40
+ /**
41
+ * Social/OAuth login buttons rendered ONCE below the active form.
42
+ * Shared between both modes (OAuth works for either flow).
43
+ */
44
+ socialLogins?: Snippet;
45
+ /**
46
+ * Override the divider label above social login buttons.
47
+ * Default: i18n key "login_or_register_form.social_divider".
48
+ * Set to `false` to hide the divider while still rendering socialLogins.
49
+ */
50
+ socialDividerLabel?: string | false;
51
+ /**
52
+ * Footer snippet. Receives the current mode + setter so consumers can
53
+ * render mode-aware content (e.g., "Already have an account? Sign in").
54
+ */
55
+ footer?: Snippet<[
56
+ {
57
+ mode: LoginOrRegisterFormMode;
58
+ setMode: (m: LoginOrRegisterFormMode) => void;
59
+ }
60
+ ]>;
61
+ notifications?: NotificationsStack;
62
+ t?: TranslateFn;
63
+ unstyled?: boolean;
64
+ class?: string;
65
+ }
66
+ declare const LoginOrRegisterForm: import("svelte").Component<Props, {}, "mode" | "loginData" | "registerData">;
67
+ type LoginOrRegisterForm = ReturnType<typeof LoginOrRegisterForm>;
68
+ export default LoginOrRegisterForm;
@@ -0,0 +1,184 @@
1
+ <script lang="ts" module>
2
+ import type { Snippet } from "svelte";
3
+ import type { TranslateFn } from "../../types.js";
4
+ import type {
5
+ LoginOrRegisterFormMode,
6
+ Props as InnerProps,
7
+ } from "./LoginOrRegisterForm.svelte";
8
+ import type { LoginFormData } from "../LoginForm/_internal/login-form-types.js";
9
+ import type { RegisterFormData } from "../RegisterForm/_internal/register-form-types.js";
10
+ import type { NotificationsStack } from "../Notifications/notifications-stack.svelte.js";
11
+
12
+ export interface Props {
13
+ /** Bindable active mode. Default: "login" */
14
+ mode?: LoginOrRegisterFormMode;
15
+
16
+ /** Bindable login formData */
17
+ loginData?: LoginFormData;
18
+
19
+ /** Bindable register formData */
20
+ registerData?: RegisterFormData;
21
+
22
+ onLogin: (data: LoginFormData) => void;
23
+ onRegister: (data: RegisterFormData) => void;
24
+
25
+ isSubmitting?: boolean;
26
+
27
+ loginProps?: InnerProps["loginProps"];
28
+ registerProps?: InnerProps["registerProps"];
29
+
30
+ modeSwitcher?: InnerProps["modeSwitcher"];
31
+
32
+ loginModeLabel?: string;
33
+ registerModeLabel?: string;
34
+
35
+ /** Shared social logins (rendered below the active form). */
36
+ socialLogins?: Snippet;
37
+ socialDividerLabel?: string | false;
38
+
39
+ /** Footer snippet. Receives mode + setter. */
40
+ footer?: InnerProps["footer"];
41
+
42
+ notifications?: NotificationsStack;
43
+
44
+ /**
45
+ * Override the modal title. If omitted, a mode-aware default is used
46
+ * ("Log In" vs "Create account").
47
+ */
48
+ title?: string;
49
+
50
+ /** Bindable modal visibility */
51
+ visible?: boolean;
52
+
53
+ /** Optional trigger element rendered outside the modal. */
54
+ trigger?: Snippet<[{ open: () => void }]>;
55
+
56
+ /** CSS class for the Modal box */
57
+ classModal?: string;
58
+
59
+ /** CSS class for the Modal inner width container */
60
+ classInner?: string;
61
+
62
+ /** CSS class for the LoginOrRegisterForm */
63
+ classForm?: string;
64
+
65
+ t?: TranslateFn;
66
+ unstyled?: boolean;
67
+
68
+ noXClose?: boolean;
69
+ onClose?: () => false | void;
70
+ }
71
+ </script>
72
+
73
+ <script lang="ts">
74
+ import Modal from "../Modal/Modal.svelte";
75
+ import LoginOrRegisterForm from "./LoginOrRegisterForm.svelte";
76
+ import Button from "../Button/Button.svelte";
77
+ import { t_default } from "./_internal/login-or-register-form-i18n-defaults.js";
78
+ import { createEmptyLoginFormData } from "../LoginForm/_internal/login-form-utils.js";
79
+ import { createEmptyRegisterFormData } from "../RegisterForm/_internal/register-form-utils.js";
80
+ import { twMerge } from "../../utils/tw-merge.js";
81
+ import H from "../H/H.svelte";
82
+
83
+ let {
84
+ mode = $bindable("login"),
85
+ loginData = $bindable(createEmptyLoginFormData()),
86
+ registerData = $bindable(createEmptyRegisterFormData()),
87
+ onLogin,
88
+ onRegister,
89
+ isSubmitting = false,
90
+ loginProps,
91
+ registerProps,
92
+ modeSwitcher,
93
+ loginModeLabel,
94
+ registerModeLabel,
95
+ socialLogins,
96
+ socialDividerLabel,
97
+ footer,
98
+ notifications,
99
+ title,
100
+ visible = $bindable(false),
101
+ trigger,
102
+ classModal,
103
+ classInner,
104
+ classForm,
105
+ t: tProp,
106
+ unstyled = false,
107
+ noXClose = false,
108
+ onClose,
109
+ }: Props = $props();
110
+
111
+ let t = $derived(tProp ?? t_default);
112
+
113
+ let resolvedTitle = $derived(
114
+ title ??
115
+ (mode === "login"
116
+ ? t("login_or_register_form.modal_title_login")
117
+ : t("login_or_register_form.modal_title_register"))
118
+ );
119
+
120
+ let modal: Modal = $state()!;
121
+
122
+ export function open(openerOrEvent?: null | HTMLElement | MouseEvent) {
123
+ modal.open(openerOrEvent);
124
+ }
125
+
126
+ export function close() {
127
+ modal.close();
128
+ }
129
+ </script>
130
+
131
+ {#if trigger}
132
+ {@render trigger({ open: (e?: MouseEvent) => modal.open(e) })}
133
+ {/if}
134
+
135
+ <Modal
136
+ bind:this={modal}
137
+ bind:visible
138
+ class={classModal}
139
+ classInner={twMerge("max-w-sm md:max-w-sm", "h-auto md:h-auto m-auto", classInner)}
140
+ classDialog="flex items-center justify-center"
141
+ >
142
+ {#snippet header()}
143
+ <div class="flex items-center justify-between p-4 pb-0">
144
+ <H level={1} renderLevel={3} class="pl-2">
145
+ {resolvedTitle}
146
+ </H>
147
+ {#if !noXClose}
148
+ <Button
149
+ variant="ghost"
150
+ onclick={() => {
151
+ if (onClose?.() === false) return;
152
+ modal.close();
153
+ }}
154
+ aria-label="Close"
155
+ x
156
+ iconButton
157
+ />
158
+ {/if}
159
+ </div>
160
+ {/snippet}
161
+
162
+ <div class="p-6 pt-3">
163
+ <LoginOrRegisterForm
164
+ bind:mode
165
+ bind:loginData
166
+ bind:registerData
167
+ {onLogin}
168
+ {onRegister}
169
+ {isSubmitting}
170
+ {loginProps}
171
+ {registerProps}
172
+ {modeSwitcher}
173
+ {loginModeLabel}
174
+ {registerModeLabel}
175
+ {socialLogins}
176
+ {socialDividerLabel}
177
+ {footer}
178
+ {notifications}
179
+ t={tProp}
180
+ {unstyled}
181
+ class={classForm}
182
+ />
183
+ </div>
184
+ </Modal>
@@ -0,0 +1,55 @@
1
+ import type { Snippet } from "svelte";
2
+ import type { TranslateFn } from "../../types.js";
3
+ import type { LoginOrRegisterFormMode, Props as InnerProps } from "./LoginOrRegisterForm.svelte";
4
+ import type { LoginFormData } from "../LoginForm/_internal/login-form-types.js";
5
+ import type { RegisterFormData } from "../RegisterForm/_internal/register-form-types.js";
6
+ import type { NotificationsStack } from "../Notifications/notifications-stack.svelte.js";
7
+ export interface Props {
8
+ /** Bindable active mode. Default: "login" */
9
+ mode?: LoginOrRegisterFormMode;
10
+ /** Bindable login formData */
11
+ loginData?: LoginFormData;
12
+ /** Bindable register formData */
13
+ registerData?: RegisterFormData;
14
+ onLogin: (data: LoginFormData) => void;
15
+ onRegister: (data: RegisterFormData) => void;
16
+ isSubmitting?: boolean;
17
+ loginProps?: InnerProps["loginProps"];
18
+ registerProps?: InnerProps["registerProps"];
19
+ modeSwitcher?: InnerProps["modeSwitcher"];
20
+ loginModeLabel?: string;
21
+ registerModeLabel?: string;
22
+ /** Shared social logins (rendered below the active form). */
23
+ socialLogins?: Snippet;
24
+ socialDividerLabel?: string | false;
25
+ /** Footer snippet. Receives mode + setter. */
26
+ footer?: InnerProps["footer"];
27
+ notifications?: NotificationsStack;
28
+ /**
29
+ * Override the modal title. If omitted, a mode-aware default is used
30
+ * ("Log In" vs "Create account").
31
+ */
32
+ title?: string;
33
+ /** Bindable modal visibility */
34
+ visible?: boolean;
35
+ /** Optional trigger element rendered outside the modal. */
36
+ trigger?: Snippet<[{
37
+ open: () => void;
38
+ }]>;
39
+ /** CSS class for the Modal box */
40
+ classModal?: string;
41
+ /** CSS class for the Modal inner width container */
42
+ classInner?: string;
43
+ /** CSS class for the LoginOrRegisterForm */
44
+ classForm?: string;
45
+ t?: TranslateFn;
46
+ unstyled?: boolean;
47
+ noXClose?: boolean;
48
+ onClose?: () => false | void;
49
+ }
50
+ declare const LoginOrRegisterFormModal: import("svelte").Component<Props, {
51
+ open: (openerOrEvent?: null | HTMLElement | MouseEvent) => void;
52
+ close: () => void;
53
+ }, "visible" | "mode" | "loginData" | "registerData">;
54
+ type LoginOrRegisterFormModal = ReturnType<typeof LoginOrRegisterFormModal>;
55
+ export default LoginOrRegisterFormModal;
@@ -0,0 +1 @@
1
+ export declare function t_default(k: string, values?: false | null | undefined | Record<string, string | number>, fallback?: string | boolean): string;
@@ -0,0 +1,17 @@
1
+ import { isPlainObject } from "../../../utils/is-plain-object.js";
2
+ import { replaceMap } from "../../../utils/replace-map.js";
3
+ const DEFAULTS = {
4
+ "login_or_register_form.mode_login": "Log in",
5
+ "login_or_register_form.mode_register": "Sign up",
6
+ "login_or_register_form.social_divider": "or continue with",
7
+ "login_or_register_form.modal_title_login": "Log In",
8
+ "login_or_register_form.modal_title_register": "Create account",
9
+ };
10
+ export function t_default(k, values = null, fallback = "") {
11
+ const out = DEFAULTS[k] ?? (typeof fallback === "string" ? fallback : "") ?? k;
12
+ return isPlainObject(values)
13
+ ? replaceMap(out, values, {
14
+ preSearchKeyTransform: (k) => `{${k}}`,
15
+ })
16
+ : out;
17
+ }
@@ -0,0 +1,66 @@
1
+ :root {
2
+ /* LoginOrRegisterForm */
3
+ --stuic-login-or-register-form-gap: 1rem;
4
+ --stuic-login-or-register-form-switcher-margin-bottom: 1rem;
5
+
6
+ /* Social login section (shared, rendered at composite level) */
7
+ --stuic-login-or-register-form-social-margin-top: 1rem;
8
+ --stuic-login-or-register-form-social-gap: 0.75rem;
9
+ --stuic-login-or-register-form-social-divider-color: var(
10
+ --stuic-color-muted-foreground
11
+ );
12
+ --stuic-login-or-register-form-social-divider-line-color: var(--stuic-color-border);
13
+ --stuic-login-or-register-form-social-divider-font-size: var(--text-sm);
14
+ --stuic-login-or-register-form-social-divider-margin-bottom: 0.75rem;
15
+ }
16
+
17
+ @layer components {
18
+ .stuic-login-or-register-form {
19
+ display: flex;
20
+ flex-direction: column;
21
+ gap: var(--stuic-login-or-register-form-gap);
22
+ }
23
+
24
+ .stuic-login-or-register-form-switcher {
25
+ display: flex;
26
+ justify-content: center;
27
+ margin-bottom: var(--stuic-login-or-register-form-switcher-margin-bottom);
28
+
29
+ .stuic-button-group-radio {
30
+ width: 100%;
31
+ }
32
+ }
33
+
34
+ .stuic-login-or-register-form-body {
35
+ display: contents;
36
+ }
37
+
38
+ /* Social login container */
39
+ .stuic-login-or-register-form-social {
40
+ margin-top: var(--stuic-login-or-register-form-social-margin-top);
41
+ }
42
+
43
+ /* "or continue with" divider */
44
+ .stuic-login-or-register-form-social-divider {
45
+ display: flex;
46
+ align-items: center;
47
+ gap: 1rem;
48
+ margin-bottom: var(--stuic-login-or-register-form-social-divider-margin-bottom);
49
+ color: var(--stuic-login-or-register-form-social-divider-color);
50
+ font-size: var(--stuic-login-or-register-form-social-divider-font-size);
51
+ }
52
+
53
+ .stuic-login-or-register-form-social-divider::before,
54
+ .stuic-login-or-register-form-social-divider::after {
55
+ content: "";
56
+ flex: 1;
57
+ border-top: 1px solid var(--stuic-login-or-register-form-social-divider-line-color);
58
+ }
59
+
60
+ /* Button container */
61
+ .stuic-login-or-register-form-social-buttons {
62
+ display: flex;
63
+ flex-direction: column;
64
+ gap: var(--stuic-login-or-register-form-social-gap);
65
+ }
66
+ }
@@ -0,0 +1,2 @@
1
+ export { default as LoginOrRegisterForm, type Props as LoginOrRegisterFormProps, type LoginOrRegisterFormMode, } from "./LoginOrRegisterForm.svelte";
2
+ export { default as LoginOrRegisterFormModal, type Props as LoginOrRegisterFormModalProps, } from "./LoginOrRegisterFormModal.svelte";
@@ -0,0 +1,2 @@
1
+ export { default as LoginOrRegisterForm, } from "./LoginOrRegisterForm.svelte";
2
+ export { default as LoginOrRegisterFormModal, } from "./LoginOrRegisterFormModal.svelte";