@replicated/portal-components 0.0.1

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.
@@ -0,0 +1,273 @@
1
+ {
2
+ "version": "0.0.1",
3
+ "generatedAt": "2025-02-09T00:00:00.000Z",
4
+ "components": [
5
+ {
6
+ "name": "Button",
7
+ "displayName": "Button",
8
+ "versionIntroduced": "0.0.1",
9
+ "stability": "beta",
10
+ "category": "primitive",
11
+ "tags": [
12
+ "action",
13
+ "cta",
14
+ "form"
15
+ ],
16
+ "description": "Theme-aware CTA for triggering portal actions with optional icons and loading feedback.",
17
+ "props": [
18
+ {
19
+ "name": "variant",
20
+ "type": "\"primary\" | \"secondary\" | \"ghost\" | \"destructive\"",
21
+ "required": false,
22
+ "defaultValue": "\"primary\"",
23
+ "description": "Controls the color treatment to match semantic intent (primary for CTAs, ghost for subdued actions).",
24
+ "constraints": {
25
+ "allowedValues": [
26
+ "primary",
27
+ "secondary",
28
+ "ghost",
29
+ "destructive"
30
+ ]
31
+ }
32
+ },
33
+ {
34
+ "name": "size",
35
+ "type": "\"sm\" | \"md\" | \"lg\"",
36
+ "required": false,
37
+ "defaultValue": "\"md\"",
38
+ "description": "Adjusts vertical rhythm and padding to align with form density.",
39
+ "constraints": {
40
+ "allowedValues": [
41
+ "sm",
42
+ "md",
43
+ "lg"
44
+ ]
45
+ }
46
+ },
47
+ {
48
+ "name": "isLoading",
49
+ "type": "boolean",
50
+ "required": false,
51
+ "defaultValue": "false",
52
+ "description": "Replaces the leading icon slot with a spinner and disables the control until the action settles."
53
+ },
54
+ {
55
+ "name": "leadingIcon",
56
+ "type": "ReactNode",
57
+ "required": false,
58
+ "description": "Icon rendered before the label (ignored while loading)."
59
+ },
60
+ {
61
+ "name": "trailingIcon",
62
+ "type": "ReactNode",
63
+ "required": false,
64
+ "description": "Icon rendered after the label for directional affordances."
65
+ },
66
+ {
67
+ "name": "children",
68
+ "type": "ReactNode",
69
+ "required": true,
70
+ "description": "Button label describing the action. Keep concise."
71
+ },
72
+ {
73
+ "name": "disabled",
74
+ "type": "boolean",
75
+ "required": false,
76
+ "description": "Disables the button (automatically set when `isLoading` is true)."
77
+ },
78
+ {
79
+ "name": "type",
80
+ "type": "\"button\" | \"submit\" | \"reset\"",
81
+ "required": false,
82
+ "defaultValue": "\"button\"",
83
+ "description": "HTML button type. Use `submit` inside React Hook Form actions."
84
+ }
85
+ ],
86
+ "composition": {
87
+ "requiredParents": [],
88
+ "requiredChildren": [],
89
+ "forbiddenChildren": [],
90
+ "slots": [
91
+ "leadingIcon",
92
+ "label",
93
+ "trailingIcon"
94
+ ]
95
+ },
96
+ "examples": [
97
+ {
98
+ "id": "primary-save",
99
+ "description": "Primary CTA for saving entitlement edits.",
100
+ "scenario": "Server-action form submission from license edit modal.",
101
+ "code": "<Button type=\"submit\" variant=\"primary\">Save changes</Button>"
102
+ },
103
+ {
104
+ "id": "ghost-table-action",
105
+ "description": "Ghost button for inline table actions where emphasis should stay on row data.",
106
+ "scenario": "Row-level action menu expansion.",
107
+ "code": "<Button variant=\"ghost\" size=\"sm\" leadingIcon={<IconEllipsis />}>More</Button>"
108
+ },
109
+ {
110
+ "id": "loading-state",
111
+ "description": "Show async progress when invoking a Server Action.",
112
+ "scenario": "Triggering `rotateLicenseKey` via `useTransition`.",
113
+ "code": "<Button variant=\"secondary\" isLoading>Rotating key…</Button>"
114
+ }
115
+ ],
116
+ "behavior": {
117
+ "mode": "client",
118
+ "requiresJavaScript": true,
119
+ "dataAccess": "none",
120
+ "managesState": "local-state"
121
+ },
122
+ "accessibility": {
123
+ "aria": [
124
+ "`aria-busy` flips on during loading for screen reader context.",
125
+ "Button remains focusable while loading unless disabled."
126
+ ],
127
+ "keyboard": [
128
+ "Supports default Enter/Space activation.",
129
+ "Focus ring meets WCAG 2.1 AA contrast (uses portal tokens)."
130
+ ],
131
+ "screenReader": [
132
+ "Loading spinner is marked `aria-hidden`, users rely on `aria-busy` for announcement."
133
+ ],
134
+ "focusManagement": [
135
+ "Do not automatically move focus away; allow users to tab away when loading."
136
+ ]
137
+ },
138
+ "performance": {
139
+ "bundleImpact": "low",
140
+ "notes": "Renders minimal markup and only includes a CSS-based spinner. Tree-shakes cleanly.",
141
+ "lazyLoadable": false,
142
+ "virtualization": false
143
+ },
144
+ "relatedComponents": []
145
+ },
146
+ {
147
+ "name": "Login",
148
+ "displayName": "Login",
149
+ "versionIntroduced": "0.0.1",
150
+ "stability": "experimental",
151
+ "category": "pattern",
152
+ "tags": [
153
+ "auth",
154
+ "form",
155
+ "card"
156
+ ],
157
+ "description": "Bordered authentication form with centered branding, email capture, and CTA button built for initial portal logins.",
158
+ "props": [
159
+ {
160
+ "name": "logo",
161
+ "type": "ReactNode",
162
+ "required": false,
163
+ "description": "Optional logo rendered above the title. Defaults to a stylized EP monogram."
164
+ },
165
+ {
166
+ "name": "title",
167
+ "type": "string",
168
+ "required": false,
169
+ "defaultValue": "\"Enterprise Factory Installation Portal\"",
170
+ "description": "Headline displayed under the logo."
171
+ },
172
+ {
173
+ "name": "description",
174
+ "type": "string",
175
+ "required": false,
176
+ "defaultValue": "\"Sign in to manage your enterprise installation\"",
177
+ "description": "Supporting copy below the title."
178
+ },
179
+ {
180
+ "name": "label",
181
+ "type": "string",
182
+ "required": false,
183
+ "defaultValue": "\"Work email address\"",
184
+ "description": "Accessible label for the email input."
185
+ },
186
+ {
187
+ "name": "placeholder",
188
+ "type": "string",
189
+ "required": false,
190
+ "defaultValue": "\"you@company.com\"",
191
+ "description": "Placeholder text inside the email field."
192
+ },
193
+ {
194
+ "name": "ctaLabel",
195
+ "type": "string",
196
+ "required": false,
197
+ "defaultValue": "\"Continue with email →\"",
198
+ "description": "Copy shown inside the submit button."
199
+ },
200
+ {
201
+ "name": "initialEmail",
202
+ "type": "string",
203
+ "required": false,
204
+ "description": "Prefills the input when the user already provided their email."
205
+ },
206
+ {
207
+ "name": "isSubmitting",
208
+ "type": "boolean",
209
+ "required": false,
210
+ "defaultValue": "false",
211
+ "description": "When true the form disables interactions and shows the button spinner."
212
+ },
213
+ {
214
+ "name": "onContinue",
215
+ "type": "(email: string) => Promise<void> | void",
216
+ "required": false,
217
+ "description": "Callback invoked when the form submits. Receives the current email string."
218
+ }
219
+ ],
220
+ "composition": {
221
+ "requiredParents": [],
222
+ "requiredChildren": [],
223
+ "forbiddenChildren": [],
224
+ "slots": [
225
+ "logo"
226
+ ]
227
+ },
228
+ "examples": [
229
+ {
230
+ "id": "default-login",
231
+ "description": "Default login experience with built-in copy.",
232
+ "scenario": "Entry point for portal authentication.",
233
+ "code": "<Login onContinue={async (email) => createSession(email)} />"
234
+ },
235
+ {
236
+ "id": "custom-logo",
237
+ "description": "Override the logo with a vendor-specific mark.",
238
+ "scenario": "White-labeled portal for an ISV.",
239
+ "code": "<Login logo={<VendorLogo />} title=\"Acme Deployment Portal\" description=\"Access licenses, releases, and support\" />"
240
+ }
241
+ ],
242
+ "behavior": {
243
+ "mode": "client",
244
+ "requiresJavaScript": true,
245
+ "dataAccess": "none",
246
+ "managesState": "local-state"
247
+ },
248
+ "accessibility": {
249
+ "aria": [
250
+ "Labels the email input explicitly, and button uses Spinner aria-hidden content."
251
+ ],
252
+ "keyboard": [
253
+ "Form supports Enter submission; button focus ring mirrors Button component."
254
+ ],
255
+ "screenReader": [
256
+ "Announces loading state via disabled button text and spinner while submitting."
257
+ ],
258
+ "focusManagement": [
259
+ "Maintains focus on email field until submission completes, preventing unexpected jumps."
260
+ ]
261
+ },
262
+ "performance": {
263
+ "bundleImpact": "low",
264
+ "notes": "Wraps Button and a small controlled input; no external dependencies.",
265
+ "lazyLoadable": true,
266
+ "virtualization": false
267
+ },
268
+ "relatedComponents": [
269
+ "Button"
270
+ ]
271
+ }
272
+ ]
273
+ }
@@ -0,0 +1,77 @@
1
+ # Component Registry
2
+
3
+ - Version: 0.0.1
4
+ - Generated: 2025-02-09T00:00:00.000Z
5
+
6
+ ## Button
7
+
8
+ Theme-aware CTA for triggering portal actions with optional icons and loading feedback.
9
+
10
+ **Category:** primitive
11
+ **Stability:** beta
12
+
13
+ | Prop | Type | Required | Description |
14
+ | --- | --- | --- | --- |
15
+ | variant | `"primary" \| "secondary" \| "ghost" \| "destructive"` | No | Controls the color treatment to match semantic intent (primary for CTAs, ghost for subdued actions). |
16
+ | size | `"sm" \| "md" \| "lg"` | No | Adjusts vertical rhythm and padding to align with form density. |
17
+ | isLoading | `boolean` | No | Replaces the leading icon slot with a spinner and disables the control until the action settles. |
18
+ | leadingIcon | `ReactNode` | No | Icon rendered before the label (ignored while loading). |
19
+ | trailingIcon | `ReactNode` | No | Icon rendered after the label for directional affordances. |
20
+ | children | `ReactNode` | Yes | Button label describing the action. Keep concise. |
21
+ | disabled | `boolean` | No | Disables the button (automatically set when `isLoading` is true). |
22
+ | type | `"button" \| "submit" \| "reset"` | No | HTML button type. Use `submit` inside React Hook Form actions. |
23
+
24
+ **Examples**
25
+
26
+ **Primary CTA for saving entitlement edits.**
27
+
28
+ ```tsx
29
+ <Button type="submit" variant="primary">Save changes</Button>
30
+ ```
31
+
32
+ **Ghost button for inline table actions where emphasis should stay on row data.**
33
+
34
+ ```tsx
35
+ <Button variant="ghost" size="sm" leadingIcon={<IconEllipsis />}>More</Button>
36
+ ```
37
+
38
+ **Show async progress when invoking a Server Action.**
39
+
40
+ ```tsx
41
+ <Button variant="secondary" isLoading>Rotating key…</Button>
42
+ ```
43
+
44
+ ---
45
+
46
+ ## Login
47
+
48
+ Bordered authentication form with centered branding, email capture, and CTA button built for initial portal logins.
49
+
50
+ **Category:** pattern
51
+ **Stability:** experimental
52
+
53
+ | Prop | Type | Required | Description |
54
+ | --- | --- | --- | --- |
55
+ | logo | `ReactNode` | No | Optional logo rendered above the title. Defaults to a stylized EP monogram. |
56
+ | title | `string` | No | Headline displayed under the logo. |
57
+ | description | `string` | No | Supporting copy below the title. |
58
+ | label | `string` | No | Accessible label for the email input. |
59
+ | placeholder | `string` | No | Placeholder text inside the email field. |
60
+ | ctaLabel | `string` | No | Copy shown inside the submit button. |
61
+ | initialEmail | `string` | No | Prefills the input when the user already provided their email. |
62
+ | isSubmitting | `boolean` | No | When true the form disables interactions and shows the button spinner. |
63
+ | onContinue | `(email: string) => Promise<void> \| void` | No | Callback invoked when the form submits. Receives the current email string. |
64
+
65
+ **Examples**
66
+
67
+ **Default login experience with built-in copy.**
68
+
69
+ ```tsx
70
+ <Login onContinue={async (email) => createSession(email)} />
71
+ ```
72
+
73
+ **Override the logo with a vendor-specific mark.**
74
+
75
+ ```tsx
76
+ <Login logo={<VendorLogo />} title="Acme Deployment Portal" description="Access licenses, releases, and support" />
77
+ ```
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Light-weight type helpers for defining Server Actions that align with the
3
+ * enterprise portal guardrails. The component library does not implement
4
+ * specific actions, but it exports helpers so downstream portals can describe
5
+ * their actions with consistent metadata.
6
+ */
7
+ type PortalActionVisibility = "vendor" | "customer";
8
+ interface PortalActionContext {
9
+ vendorId: string;
10
+ licenseId: string;
11
+ userId: string;
12
+ signal?: AbortSignal;
13
+ }
14
+ interface PortalServerActionDefinition<Input, Output> {
15
+ id: string;
16
+ description: string;
17
+ visibility: PortalActionVisibility;
18
+ tags: string[];
19
+ run: (input: Input, context: PortalActionContext) => Promise<Output>;
20
+ }
21
+ declare const defineServerAction: <Input, Output>(definition: PortalServerActionDefinition<Input, Output>) => PortalServerActionDefinition<Input, Output>;
22
+
23
+ export { type PortalActionContext, type PortalActionVisibility, type PortalServerActionDefinition, defineServerAction };
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Light-weight type helpers for defining Server Actions that align with the
3
+ * enterprise portal guardrails. The component library does not implement
4
+ * specific actions, but it exports helpers so downstream portals can describe
5
+ * their actions with consistent metadata.
6
+ */
7
+ type PortalActionVisibility = "vendor" | "customer";
8
+ interface PortalActionContext {
9
+ vendorId: string;
10
+ licenseId: string;
11
+ userId: string;
12
+ signal?: AbortSignal;
13
+ }
14
+ interface PortalServerActionDefinition<Input, Output> {
15
+ id: string;
16
+ description: string;
17
+ visibility: PortalActionVisibility;
18
+ tags: string[];
19
+ run: (input: Input, context: PortalActionContext) => Promise<Output>;
20
+ }
21
+ declare const defineServerAction: <Input, Output>(definition: PortalServerActionDefinition<Input, Output>) => PortalServerActionDefinition<Input, Output>;
22
+
23
+ export { type PortalActionContext, type PortalActionVisibility, type PortalServerActionDefinition, defineServerAction };
@@ -0,0 +1,13 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Enterprise Portal Components
5
+ * This file is generated by tsup. Do not edit manually.
6
+ */
7
+
8
+ // src/actions/index.ts
9
+ var defineServerAction = (definition) => definition;
10
+
11
+ exports.defineServerAction = defineServerAction;
12
+ //# sourceMappingURL=index.js.map
13
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/actions/index.ts"],"names":[],"mappings":";;;;;;;;AAwBO,IAAM,kBAAA,GAAqB,CAChC,UAAA,KACG","file":"index.js","sourcesContent":["/**\n * Light-weight type helpers for defining Server Actions that align with the\n * enterprise portal guardrails. The component library does not implement\n * specific actions, but it exports helpers so downstream portals can describe\n * their actions with consistent metadata.\n */\n\nexport type PortalActionVisibility = \"vendor\" | \"customer\";\n\nexport interface PortalActionContext {\n vendorId: string;\n licenseId: string;\n userId: string;\n signal?: AbortSignal;\n}\n\nexport interface PortalServerActionDefinition<Input, Output> {\n id: string;\n description: string;\n visibility: PortalActionVisibility;\n tags: string[];\n run: (input: Input, context: PortalActionContext) => Promise<Output>;\n}\n\nexport const defineServerAction = <Input, Output>(\n definition: PortalServerActionDefinition<Input, Output>\n) => definition;\n"]}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Enterprise Portal Components
3
+ * This file is generated by tsup. Do not edit manually.
4
+ */
5
+
6
+ // src/actions/index.ts
7
+ var defineServerAction = (definition) => definition;
8
+
9
+ export { defineServerAction };
10
+ //# sourceMappingURL=index.js.map
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/actions/index.ts"],"names":[],"mappings":";;;;;;AAwBO,IAAM,kBAAA,GAAqB,CAChC,UAAA,KACG","file":"index.js","sourcesContent":["/**\n * Light-weight type helpers for defining Server Actions that align with the\n * enterprise portal guardrails. The component library does not implement\n * specific actions, but it exports helpers so downstream portals can describe\n * their actions with consistent metadata.\n */\n\nexport type PortalActionVisibility = \"vendor\" | \"customer\";\n\nexport interface PortalActionContext {\n vendorId: string;\n licenseId: string;\n userId: string;\n signal?: AbortSignal;\n}\n\nexport interface PortalServerActionDefinition<Input, Output> {\n id: string;\n description: string;\n visibility: PortalActionVisibility;\n tags: string[];\n run: (input: Input, context: PortalActionContext) => Promise<Output>;\n}\n\nexport const defineServerAction = <Input, Output>(\n definition: PortalServerActionDefinition<Input, Output>\n) => definition;\n"]}
@@ -0,0 +1,225 @@
1
+ import { forwardRef, useState } from 'react';
2
+ import { jsxs, jsx } from 'react/jsx-runtime';
3
+
4
+ /**
5
+ * Enterprise Portal Components
6
+ * This file is generated by tsup. Do not edit manually.
7
+ */
8
+
9
+ // package.json
10
+ var package_default = {
11
+ version: "0.0.1"};
12
+
13
+ // src/tokens/index.ts
14
+ var baseTokens = {
15
+ colors: {
16
+ background: "#0b0d12",
17
+ foreground: "#f4f6fb",
18
+ surfaceMuted: "#1b1f2a",
19
+ primary: "#0d6efd",
20
+ onPrimary: "#ffffff",
21
+ secondary: "#6c757d",
22
+ onSecondary: "#ffffff",
23
+ success: "#22c55e",
24
+ warning: "#fbbf24",
25
+ danger: "#ef4444"
26
+ },
27
+ radii: {
28
+ lg: "16px",
29
+ md: "10px",
30
+ sm: "6px"
31
+ },
32
+ spacing: {
33
+ xs: "0.25rem",
34
+ sm: "0.5rem",
35
+ md: "0.75rem",
36
+ lg: "1rem",
37
+ xl: "1.5rem"
38
+ },
39
+ typography: {
40
+ fontFamily: "Inter, 'SF Pro Display', 'Segoe UI', system-ui, -apple-system, sans-serif",
41
+ monoFamily: "'JetBrains Mono', 'SFMono-Regular', Consolas, monospace",
42
+ lineHeight: {
43
+ tight: "1.2",
44
+ normal: "1.5",
45
+ relaxed: "1.75"
46
+ }
47
+ },
48
+ shadows: {
49
+ focus: "0 0 0 3px color-mix(in srgb, #0d6efd 35%, transparent)",
50
+ overlay: "0px 20px 55px rgba(5, 7, 11, 0.5)"
51
+ }
52
+ };
53
+ var mergeTokens = (base, overrides) => {
54
+ if (!overrides) {
55
+ return base;
56
+ }
57
+ const clone = structuredClone(base);
58
+ const apply = (target, source) => {
59
+ Object.entries(source).forEach(([key, value]) => {
60
+ if (value && typeof value === "object" && !Array.isArray(value) && typeof target[key] === "object") {
61
+ apply(target[key], value);
62
+ return;
63
+ }
64
+ target[key] = value;
65
+ });
66
+ };
67
+ apply(clone, overrides);
68
+ return clone;
69
+ };
70
+ var portalThemeTokens = mergeTokens(baseTokens);
71
+ var createPortalTheme = (overrides) => mergeTokens(baseTokens, overrides);
72
+ var variantStyles = {
73
+ primary: "bg-primary text-primary-foreground hover:bg-primary/90 focus-visible:ring-primary",
74
+ secondary: "bg-secondary/20 text-secondary-foreground hover:bg-secondary/30 focus-visible:ring-secondary",
75
+ ghost: "bg-transparent text-primary hover:bg-primary/10 focus-visible:ring-primary/60",
76
+ destructive: "bg-danger text-white hover:bg-danger/90 focus-visible:ring-danger"
77
+ };
78
+ var sizeStyles = {
79
+ sm: "h-8 px-3 text-sm",
80
+ md: "h-10 px-4 text-sm",
81
+ lg: "h-12 px-6 text-base"
82
+ };
83
+ var inlineFlexBase = "inline-flex items-center justify-center gap-2 rounded-md font-medium tracking-tight transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-60";
84
+ var Spinner = () => /* @__PURE__ */ jsx("span", { className: "inline-flex h-3.5 w-3.5 animate-spin items-center justify-center", children: /* @__PURE__ */ jsx("span", { className: "h-3 w-3 rounded-full border-2 border-transparent border-t-current" }) });
85
+ var composeClassName = (...values) => values.filter(Boolean).join(" ");
86
+ var Button = forwardRef(
87
+ ({
88
+ variant = "primary",
89
+ size = "md",
90
+ type = "button",
91
+ isLoading = false,
92
+ leadingIcon,
93
+ trailingIcon,
94
+ disabled,
95
+ className,
96
+ children,
97
+ ...props
98
+ }, ref) => {
99
+ const computedLeading = isLoading ? /* @__PURE__ */ jsx(Spinner, {}) : leadingIcon;
100
+ const computedDisabled = disabled ?? isLoading;
101
+ return /* @__PURE__ */ jsxs(
102
+ "button",
103
+ {
104
+ ref,
105
+ type,
106
+ className: composeClassName(
107
+ inlineFlexBase,
108
+ variantStyles[variant],
109
+ sizeStyles[size],
110
+ className
111
+ ),
112
+ "aria-busy": isLoading || void 0,
113
+ disabled: computedDisabled,
114
+ ...props,
115
+ children: [
116
+ computedLeading ? /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "inline-flex", children: computedLeading }) : null,
117
+ /* @__PURE__ */ jsx("span", { className: "flex-1 whitespace-nowrap", children }),
118
+ trailingIcon ? /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "inline-flex", children: trailingIcon }) : null
119
+ ]
120
+ }
121
+ );
122
+ }
123
+ );
124
+ Button.displayName = "Button";
125
+ var Login = forwardRef(
126
+ ({
127
+ logo,
128
+ title = "Enterprise Factory Installation Portal",
129
+ description = "Sign in to manage your enterprise installation",
130
+ label = "Work email address",
131
+ placeholder = "you@company.com",
132
+ ctaLabel = "Continue with email \u2192",
133
+ initialEmail = "",
134
+ isSubmitting = false,
135
+ onContinue,
136
+ className,
137
+ ...props
138
+ }, ref) => {
139
+ const [email, setEmail] = useState(initialEmail);
140
+ const [pending, setPending] = useState(false);
141
+ const handleSubmit = async (event) => {
142
+ event.preventDefault();
143
+ if (!onContinue) {
144
+ return;
145
+ }
146
+ try {
147
+ setPending(true);
148
+ await onContinue(email);
149
+ } finally {
150
+ setPending(false);
151
+ }
152
+ };
153
+ const disabled = isSubmitting || pending;
154
+ return /* @__PURE__ */ jsxs(
155
+ "form",
156
+ {
157
+ ref,
158
+ className: [
159
+ "w-full max-w-md rounded-3xl border border-white/15 bg-slate-950/80 p-10 shadow-[0_25px_70px_rgba(5,7,11,0.65)] backdrop-blur",
160
+ "text-white transition-shadow",
161
+ className
162
+ ].filter(Boolean).join(" "),
163
+ onSubmit: handleSubmit,
164
+ ...props,
165
+ children: [
166
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-4 text-center", children: [
167
+ logo ?? /* @__PURE__ */ jsx("div", { className: "flex h-16 w-16 items-center justify-center rounded-2xl border border-white/20 bg-white/10 text-lg font-semibold leading-tight", children: "EP" }),
168
+ /* @__PURE__ */ jsxs("div", { children: [
169
+ /* @__PURE__ */ jsx("p", { className: "text-base font-semibold uppercase tracking-[0.35em] text-white/65", children: "Enterprise Portal" }),
170
+ /* @__PURE__ */ jsx("h1", { className: "mt-3 text-3xl font-semibold tracking-tight", children: title }),
171
+ /* @__PURE__ */ jsx("p", { className: "mt-2 text-sm text-white/70", children: description })
172
+ ] })
173
+ ] }),
174
+ /* @__PURE__ */ jsxs("div", { className: "mt-8 space-y-3", children: [
175
+ /* @__PURE__ */ jsx(
176
+ "label",
177
+ {
178
+ htmlFor: "login-email",
179
+ className: "text-sm font-medium text-white/70",
180
+ children: label
181
+ }
182
+ ),
183
+ /* @__PURE__ */ jsx(
184
+ "input",
185
+ {
186
+ id: "login-email",
187
+ type: "email",
188
+ inputMode: "email",
189
+ autoComplete: "email",
190
+ placeholder,
191
+ value: email,
192
+ onChange: (event) => setEmail(event.target.value),
193
+ className: "w-full rounded-2xl border border-white/15 bg-white/5 px-5 py-4 text-base text-white placeholder:text-white/40 focus:border-white/40 focus:outline-none focus:ring-2 focus:ring-white/25",
194
+ disabled,
195
+ required: true
196
+ }
197
+ ),
198
+ /* @__PURE__ */ jsx(
199
+ Button,
200
+ {
201
+ type: "submit",
202
+ size: "lg",
203
+ className: "w-full justify-center",
204
+ disabled,
205
+ isLoading: disabled,
206
+ children: ctaLabel
207
+ }
208
+ )
209
+ ] })
210
+ ]
211
+ }
212
+ );
213
+ }
214
+ );
215
+ Login.displayName = "Login";
216
+
217
+ // src/actions/index.ts
218
+ var defineServerAction = (definition) => definition;
219
+
220
+ // src/index.ts
221
+ var portalComponentsVersion = package_default.version;
222
+
223
+ export { Button, Login, createPortalTheme, defineServerAction, portalComponentsVersion, portalThemeTokens };
224
+ //# sourceMappingURL=index.js.map
225
+ //# sourceMappingURL=index.js.map