@elvix.is/sdk 0.5.1 → 0.5.3
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/dist/react.d.ts +132 -15
- package/dist/react.js +322 -78
- package/package.json +1 -1
package/dist/react.d.ts
CHANGED
|
@@ -20,11 +20,59 @@ declare function ElvixCard({ title, footer, className, children, }: {
|
|
|
20
20
|
children: ReactNode;
|
|
21
21
|
}): react.JSX.Element;
|
|
22
22
|
|
|
23
|
+
/**
|
|
24
|
+
* Editable sign-in copy.
|
|
25
|
+
*
|
|
26
|
+
* Every user-facing string in the sign-in surface is overridable. Precedence,
|
|
27
|
+
* lowest to highest:
|
|
28
|
+
*
|
|
29
|
+
* built-in English defaults < Console-configured (bootstrap `strings`) < `copy` prop
|
|
30
|
+
*
|
|
31
|
+
* So an integrating developer edits copy in the elvix Console (no redeploy) and
|
|
32
|
+
* can still override per-embed in code. `title` and `submitButton` are left out
|
|
33
|
+
* of the defaults because their built-in value depends on the Application's
|
|
34
|
+
* sign-in verb ("Sign in" vs "Log in"); the component fills them from the verb
|
|
35
|
+
* when neither Console nor prop sets them.
|
|
36
|
+
*
|
|
37
|
+
* Strings may contain `{app}` / `{email}` tokens; `fillCopy` interpolates them.
|
|
38
|
+
*/
|
|
39
|
+
type ElvixCopy = {
|
|
40
|
+
/** Heading. Token: {app}. Built-in: "Sign in to {app}" / "Log in to {app}". */
|
|
41
|
+
title?: string;
|
|
42
|
+
/** Subtitle under the heading. */
|
|
43
|
+
subtitle?: string;
|
|
44
|
+
/** Google factor button. */
|
|
45
|
+
googleButton?: string;
|
|
46
|
+
/** Email field placeholder. */
|
|
47
|
+
emailPlaceholder?: string;
|
|
48
|
+
/** Email submit button (identify step). */
|
|
49
|
+
sendCodeButton?: string;
|
|
50
|
+
/** Email submit button while the request is in flight. */
|
|
51
|
+
sendingLabel?: string;
|
|
52
|
+
/** Code step subtitle. Token: {email}. */
|
|
53
|
+
codeSentSubtitle?: string;
|
|
54
|
+
/** OTP field placeholder. */
|
|
55
|
+
codePlaceholder?: string;
|
|
56
|
+
/** OTP submit button. Built-in: the sign-in verb. */
|
|
57
|
+
submitButton?: string;
|
|
58
|
+
/** OTP submit button while verifying. */
|
|
59
|
+
verifyingLabel?: string;
|
|
60
|
+
/** Terminal "done" pane text. */
|
|
61
|
+
signedInText?: string;
|
|
62
|
+
/** Validation: empty email. */
|
|
63
|
+
errorEnterEmail?: string;
|
|
64
|
+
/** Validation: code not 6 digits. */
|
|
65
|
+
errorEnterCode?: string;
|
|
66
|
+
};
|
|
67
|
+
/** Built-in English defaults for the verb-independent strings. */
|
|
68
|
+
declare const DEFAULT_COPY: ElvixCopy;
|
|
69
|
+
|
|
23
70
|
/**
|
|
24
71
|
* Public types for the React surface. Mirrors the elvix.is bootstrap
|
|
25
72
|
* envelope so customers can type their host code without importing
|
|
26
73
|
* private elvix internals.
|
|
27
74
|
*/
|
|
75
|
+
|
|
28
76
|
type ElvixBrand = {
|
|
29
77
|
light: {
|
|
30
78
|
primary: string;
|
|
@@ -36,6 +84,12 @@ type ElvixBrand = {
|
|
|
36
84
|
};
|
|
37
85
|
};
|
|
38
86
|
type ElvixSignInMethod = "google" | "email_otp" | "passkey" | "username";
|
|
87
|
+
/**
|
|
88
|
+
* The public render envelope `GET /api/v1/bootstrap/<clientId>` returns. Flat
|
|
89
|
+
* to match the wire shape exactly (the provider builds the {light,dark} brand
|
|
90
|
+
* chord from the colour fields). Any field here is already public — it's what
|
|
91
|
+
* the sign-in surface shows.
|
|
92
|
+
*/
|
|
39
93
|
type ElvixBootstrapEnvelope = {
|
|
40
94
|
applicationId: string;
|
|
41
95
|
clientId: string;
|
|
@@ -45,21 +99,34 @@ type ElvixBootstrapEnvelope = {
|
|
|
45
99
|
logoUrlDark: string | null;
|
|
46
100
|
iconUrl: string | null;
|
|
47
101
|
iconUrlDark: string | null;
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
102
|
+
websiteUrl: string | null;
|
|
103
|
+
privacyPolicyUrl: string;
|
|
104
|
+
termsOfServiceUrl: string;
|
|
105
|
+
supportUrl: string | null;
|
|
106
|
+
brandColor: string;
|
|
107
|
+
brandColorDark: string | null;
|
|
108
|
+
onBrandColor: string;
|
|
109
|
+
onBrandColorDark: string | null;
|
|
110
|
+
brandPreset: string;
|
|
111
|
+
methodGoogle: boolean;
|
|
112
|
+
methodEmailOtp: boolean;
|
|
113
|
+
methodPasskey: boolean;
|
|
114
|
+
methodUsername: boolean;
|
|
115
|
+
layout: string;
|
|
116
|
+
socialLayout: string;
|
|
117
|
+
presentation: string;
|
|
118
|
+
theme: "light" | "dark" | "system";
|
|
119
|
+
showHeader: boolean;
|
|
120
|
+
transparentBg: boolean;
|
|
61
121
|
signInVerb: "signin" | "login";
|
|
62
122
|
signinGate: "public" | "private_beta" | "closed";
|
|
123
|
+
archivedAt: string | null;
|
|
124
|
+
/**
|
|
125
|
+
* Console-configured sign-in copy overrides. Any subset of the strings the
|
|
126
|
+
* sign-in surface renders; missing keys fall back to the built-in English
|
|
127
|
+
* defaults. A `copy` prop on the component overrides these in turn.
|
|
128
|
+
*/
|
|
129
|
+
strings?: Partial<ElvixCopy>;
|
|
63
130
|
};
|
|
64
131
|
type ElvixSignInResultOk = {
|
|
65
132
|
ok: true;
|
|
@@ -119,13 +186,63 @@ declare function ElvixProvider({ clientId, theme, brand, baseUrl, children, clas
|
|
|
119
186
|
* exposes. Hosts navigate from the callback; this component never
|
|
120
187
|
* calls `router.push` itself.
|
|
121
188
|
*/
|
|
122
|
-
declare function ElvixSignIn({ onResult, redirectAfterSignIn, className, }: {
|
|
189
|
+
declare function ElvixSignIn({ onResult, redirectAfterSignIn, copy: copyProp, className, }: {
|
|
123
190
|
onResult?: (r: ElvixSignInResult) => void;
|
|
124
191
|
/** Default redirect target on success when the server doesn't echo one. */
|
|
125
192
|
redirectAfterSignIn?: string;
|
|
193
|
+
/**
|
|
194
|
+
* Thin per-embed copy override. The primary way to edit copy is the elvix
|
|
195
|
+
* Console (served live in the bootstrap `strings`); this prop just lets a
|
|
196
|
+
* single embed tweak a string or two without a Console change.
|
|
197
|
+
*/
|
|
198
|
+
copy?: Partial<ElvixCopy>;
|
|
126
199
|
className?: string;
|
|
127
200
|
}): react.JSX.Element;
|
|
128
201
|
|
|
202
|
+
type ElvixSignInButtonSize = "sm" | "md" | "lg";
|
|
203
|
+
type ElvixSignInButtonTheme = "light" | "dark" | "auto";
|
|
204
|
+
type ElvixSignInButtonVariant = "filled" | "filled-black" | "white" | "outline" | "ghost";
|
|
205
|
+
type ElvixSignInButtonShape = "rectangle" | "pill" | "square" | "circle";
|
|
206
|
+
type ElvixSignInButtonType = "standard" | "icon";
|
|
207
|
+
type ElvixSignInButtonMode = "redirect" | "callback" | "embed";
|
|
208
|
+
type ElvixSignInPreset = "sign-in-with-elvix" | "continue-with-elvix" | "sign-up-with-elvix" | "sign-in" | "log-in" | "continue";
|
|
209
|
+
type ElvixSignInButtonProps = {
|
|
210
|
+
clientId?: string;
|
|
211
|
+
/** elvix origin for redirect mode. Defaults to https://elvix.is. */
|
|
212
|
+
baseUrl?: string;
|
|
213
|
+
returnUrl?: string;
|
|
214
|
+
type?: ElvixSignInButtonType;
|
|
215
|
+
variant?: ElvixSignInButtonVariant;
|
|
216
|
+
shape?: ElvixSignInButtonShape;
|
|
217
|
+
size?: ElvixSignInButtonSize;
|
|
218
|
+
theme?: ElvixSignInButtonTheme;
|
|
219
|
+
preset?: ElvixSignInPreset;
|
|
220
|
+
label?: string;
|
|
221
|
+
className?: string;
|
|
222
|
+
href?: string;
|
|
223
|
+
mode?: ElvixSignInButtonMode;
|
|
224
|
+
onClick?: () => void;
|
|
225
|
+
/** Terminal outcome of mode="embed": success (with token) or error. */
|
|
226
|
+
onResult?: (result: ElvixSignInResult) => void;
|
|
227
|
+
};
|
|
228
|
+
declare function ElvixSignInButton({ clientId, baseUrl, returnUrl, type, variant, shape, size, theme, preset, label, className, href, mode, onClick, onResult, }: ElvixSignInButtonProps): react.JSX.Element;
|
|
229
|
+
|
|
230
|
+
type ElvixSecuredBadgeVariant = "white" | "dark" | "outline";
|
|
231
|
+
type ElvixSecuredBadgeSize = "sm" | "md" | "lg";
|
|
232
|
+
type ElvixSecuredBadgeTheme = "light" | "dark";
|
|
233
|
+
type ElvixSecuredBadgeProps = {
|
|
234
|
+
variant?: ElvixSecuredBadgeVariant;
|
|
235
|
+
size?: ElvixSecuredBadgeSize;
|
|
236
|
+
/** Which side of the colour wheel the host page is on (for the outline variant). */
|
|
237
|
+
theme?: ElvixSecuredBadgeTheme;
|
|
238
|
+
/** Active-protection dot colour. Defaults to brand lavender. */
|
|
239
|
+
accentColor?: string;
|
|
240
|
+
/** Where the badge links. Defaults to elvix. */
|
|
241
|
+
href?: string;
|
|
242
|
+
className?: string;
|
|
243
|
+
};
|
|
244
|
+
declare function ElvixSecuredBadge({ variant, size, theme, accentColor, href, className, }: ElvixSecuredBadgeProps): react.JSX.Element;
|
|
245
|
+
|
|
129
246
|
/**
|
|
130
247
|
* Session token store for cross-origin SDK use.
|
|
131
248
|
*
|
|
@@ -280,4 +397,4 @@ declare function ElvixLegalEntities({ onResult, }: {
|
|
|
280
397
|
onResult?: (r: ElvixActionResult) => void;
|
|
281
398
|
}): react.JSX.Element;
|
|
282
399
|
|
|
283
|
-
export { ElvixActionResult, ElvixAddressBook, ElvixAvatar, ElvixBanner, type ElvixBootstrapEnvelope, type ElvixBrand, ElvixCard, ElvixDeactivate, ElvixExport, ElvixIdentityForm, ElvixLanguages, ElvixLeave, ElvixLegalEntities, ElvixLifecycleWatcher, ElvixProvider, ElvixRegion, ElvixSessions, ElvixSignIn, type ElvixSignInMethod, type ElvixSignInResult, type ElvixSignInResultErr, type ElvixSignInResultOk, type ElvixTheme, ElvixUsername, type UseUserListResult, getElvixToken, setElvixToken, useElvixApp, useElvixContext, useUserMemberships, useUserRoles, useUserScopes };
|
|
400
|
+
export { DEFAULT_COPY, ElvixActionResult, ElvixAddressBook, ElvixAvatar, ElvixBanner, type ElvixBootstrapEnvelope, type ElvixBrand, ElvixCard, type ElvixCopy, ElvixDeactivate, ElvixExport, ElvixIdentityForm, ElvixLanguages, ElvixLeave, ElvixLegalEntities, ElvixLifecycleWatcher, ElvixProvider, ElvixRegion, ElvixSecuredBadge, ElvixSessions, ElvixSignIn, ElvixSignInButton, type ElvixSignInMethod, type ElvixSignInResult, type ElvixSignInResultErr, type ElvixSignInResultOk, type ElvixTheme, ElvixUsername, type UseUserListResult, getElvixToken, setElvixToken, useElvixApp, useElvixContext, useUserMemberships, useUserRoles, useUserScopes };
|
package/dist/react.js
CHANGED
|
@@ -151,8 +151,14 @@ function ElvixProvider({
|
|
|
151
151
|
));
|
|
152
152
|
}
|
|
153
153
|
function appBrand(app) {
|
|
154
|
-
if (!app?.
|
|
155
|
-
return
|
|
154
|
+
if (!app?.brandColor) return null;
|
|
155
|
+
return {
|
|
156
|
+
light: { primary: app.brandColor, on: app.onBrandColor },
|
|
157
|
+
dark: {
|
|
158
|
+
primary: app.brandColorDark ?? app.brandColor,
|
|
159
|
+
on: app.onBrandColorDark ?? app.onBrandColor
|
|
160
|
+
}
|
|
161
|
+
};
|
|
156
162
|
}
|
|
157
163
|
function withAlpha(hex, a) {
|
|
158
164
|
const m = /^#?([0-9a-f]{6})$/i.exec(hex.trim());
|
|
@@ -167,6 +173,42 @@ function withAlpha(hex, a) {
|
|
|
167
173
|
// src/react/elvix-sign-in.tsx
|
|
168
174
|
import { useState as useState2 } from "react";
|
|
169
175
|
|
|
176
|
+
// src/react/copy.ts
|
|
177
|
+
var DEFAULT_COPY = {
|
|
178
|
+
subtitle: "Pick how you want to continue.",
|
|
179
|
+
googleButton: "Continue with Google",
|
|
180
|
+
emailPlaceholder: "you@example.com",
|
|
181
|
+
sendCodeButton: "Send code",
|
|
182
|
+
sendingLabel: "Sending\u2026",
|
|
183
|
+
codeSentSubtitle: "We sent a 6-digit code to {email}.",
|
|
184
|
+
codePlaceholder: "123456",
|
|
185
|
+
verifyingLabel: "Verifying\u2026",
|
|
186
|
+
signedInText: "Signed in.",
|
|
187
|
+
errorEnterEmail: "Enter an email.",
|
|
188
|
+
errorEnterCode: "Enter the 6-digit code."
|
|
189
|
+
};
|
|
190
|
+
function resolveCopy(bootstrap, prop) {
|
|
191
|
+
return {
|
|
192
|
+
...DEFAULT_COPY,
|
|
193
|
+
...stripUndefined(bootstrap),
|
|
194
|
+
...stripUndefined(prop)
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
function fillCopy(template, tokens) {
|
|
198
|
+
return template.replace(
|
|
199
|
+
/\{(\w+)\}/g,
|
|
200
|
+
(whole, key) => key in tokens ? tokens[key] : whole
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
function stripUndefined(o) {
|
|
204
|
+
if (!o) return {};
|
|
205
|
+
const out = {};
|
|
206
|
+
for (const [k, v] of Object.entries(o)) {
|
|
207
|
+
if (v !== void 0) out[k] = v;
|
|
208
|
+
}
|
|
209
|
+
return out;
|
|
210
|
+
}
|
|
211
|
+
|
|
170
212
|
// src/react/session.ts
|
|
171
213
|
var STORAGE_KEY = "elvix.session.token";
|
|
172
214
|
var memToken = null;
|
|
@@ -207,10 +249,12 @@ function isSameOrigin(baseUrl) {
|
|
|
207
249
|
function ElvixSignIn({
|
|
208
250
|
onResult,
|
|
209
251
|
redirectAfterSignIn,
|
|
252
|
+
copy: copyProp,
|
|
210
253
|
className = ""
|
|
211
254
|
}) {
|
|
212
255
|
const ctx = useElvixContext();
|
|
213
256
|
const app = useElvixApp();
|
|
257
|
+
const copy = resolveCopy(app?.strings, copyProp);
|
|
214
258
|
const [step, setStep] = useState2("identify");
|
|
215
259
|
const [email, setEmail] = useState2("");
|
|
216
260
|
const [code, setCode] = useState2("");
|
|
@@ -218,6 +262,9 @@ function ElvixSignIn({
|
|
|
218
262
|
const [busy, setBusy] = useState2(false);
|
|
219
263
|
const [error, setError] = useState2(null);
|
|
220
264
|
const verb = app?.signInVerb === "login" ? "Log in" : "Sign in";
|
|
265
|
+
const defaultTitle = app?.appName ? `${verb} to ${app.appName}` : verb;
|
|
266
|
+
const title = copy.title ? fillCopy(copy.title, { app: app?.appName ?? "" }) : defaultTitle;
|
|
267
|
+
const submitLabel = copy.submitButton ?? verb;
|
|
221
268
|
function fail(error2, message) {
|
|
222
269
|
setError(message ?? error2);
|
|
223
270
|
onResult?.({ ok: false, error: error2, message });
|
|
@@ -230,7 +277,7 @@ function ElvixSignIn({
|
|
|
230
277
|
}
|
|
231
278
|
async function startOtp(e) {
|
|
232
279
|
e.preventDefault();
|
|
233
|
-
if (!email.trim()) return fail("invalid_input",
|
|
280
|
+
if (!email.trim()) return fail("invalid_input", copy.errorEnterEmail);
|
|
234
281
|
setBusy(true);
|
|
235
282
|
setError(null);
|
|
236
283
|
try {
|
|
@@ -259,7 +306,7 @@ function ElvixSignIn({
|
|
|
259
306
|
async function verifyOtp(e) {
|
|
260
307
|
e.preventDefault();
|
|
261
308
|
if (!challengeId) return;
|
|
262
|
-
if (code.trim().length !== 6) return fail("invalid_input",
|
|
309
|
+
if (code.trim().length !== 6) return fail("invalid_input", copy.errorEnterCode);
|
|
263
310
|
setBusy(true);
|
|
264
311
|
setError(null);
|
|
265
312
|
try {
|
|
@@ -289,9 +336,9 @@ function ElvixSignIn({
|
|
|
289
336
|
}
|
|
290
337
|
const card = `elvix-card ${className}`.trim();
|
|
291
338
|
if (step === "done") {
|
|
292
|
-
return /* @__PURE__ */ React.createElement("div", { className: card, "data-elvix-pane": "done" }, /* @__PURE__ */ React.createElement("p", null,
|
|
339
|
+
return /* @__PURE__ */ React.createElement("div", { className: card, "data-elvix-pane": "done" }, /* @__PURE__ */ React.createElement("p", null, copy.signedInText));
|
|
293
340
|
}
|
|
294
|
-
return /* @__PURE__ */ React.createElement("div", { className: card, "data-elvix-pane": step }, /* @__PURE__ */ React.createElement("h2", { className: "elvix-h" },
|
|
341
|
+
return /* @__PURE__ */ React.createElement("div", { className: card, "data-elvix-pane": step }, /* @__PURE__ */ React.createElement("h2", { className: "elvix-h" }, title), copy.subtitle && /* @__PURE__ */ React.createElement("p", { className: "elvix-muted elvix-subtitle" }, copy.subtitle), step === "identify" && /* @__PURE__ */ React.createElement(React.Fragment, null, app?.methodGoogle && /* @__PURE__ */ React.createElement(
|
|
295
342
|
"button",
|
|
296
343
|
{
|
|
297
344
|
type: "button",
|
|
@@ -300,19 +347,19 @@ function ElvixSignIn({
|
|
|
300
347
|
className: "elvix-btn elvix-btn-google",
|
|
301
348
|
"data-elvix-method": "google"
|
|
302
349
|
},
|
|
303
|
-
|
|
304
|
-
), app?.
|
|
350
|
+
copy.googleButton
|
|
351
|
+
), app?.methodEmailOtp && /* @__PURE__ */ React.createElement("form", { onSubmit: startOtp, "data-elvix-method": "email_otp", className: "elvix-otp-form" }, /* @__PURE__ */ React.createElement(
|
|
305
352
|
"input",
|
|
306
353
|
{
|
|
307
354
|
type: "email",
|
|
308
355
|
value: email,
|
|
309
356
|
onChange: (ev) => setEmail(ev.target.value),
|
|
310
|
-
placeholder:
|
|
357
|
+
placeholder: copy.emailPlaceholder,
|
|
311
358
|
required: true,
|
|
312
359
|
disabled: busy,
|
|
313
360
|
className: "elvix-input"
|
|
314
361
|
}
|
|
315
|
-
), /* @__PURE__ */ React.createElement("button", { type: "submit", disabled: busy, className: "elvix-btn elvix-btn-primary" }, busy ?
|
|
362
|
+
), /* @__PURE__ */ React.createElement("button", { type: "submit", disabled: busy, className: "elvix-btn elvix-btn-primary" }, busy ? copy.sendingLabel : copy.sendCodeButton))), step === "code" && /* @__PURE__ */ React.createElement("form", { onSubmit: verifyOtp, className: "elvix-otp-form" }, /* @__PURE__ */ React.createElement("p", { className: "elvix-muted" }, fillCopy(copy.codeSentSubtitle ?? "", { email })), /* @__PURE__ */ React.createElement(
|
|
316
363
|
"input",
|
|
317
364
|
{
|
|
318
365
|
type: "text",
|
|
@@ -321,22 +368,216 @@ function ElvixSignIn({
|
|
|
321
368
|
maxLength: 6,
|
|
322
369
|
value: code,
|
|
323
370
|
onChange: (ev) => setCode(ev.target.value.replace(/\D/g, "")),
|
|
324
|
-
placeholder:
|
|
371
|
+
placeholder: copy.codePlaceholder,
|
|
325
372
|
required: true,
|
|
326
373
|
disabled: busy,
|
|
327
374
|
className: "elvix-input"
|
|
328
375
|
}
|
|
329
|
-
), /* @__PURE__ */ React.createElement("button", { type: "submit", disabled: busy, className: "elvix-btn elvix-btn-primary" }, busy ?
|
|
376
|
+
), /* @__PURE__ */ React.createElement("button", { type: "submit", disabled: busy, className: "elvix-btn elvix-btn-primary" }, busy ? copy.verifyingLabel : submitLabel)), error && /* @__PURE__ */ React.createElement("p", { role: "alert", className: "elvix-error" }, error));
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// src/react/elvix-sign-in-button.tsx
|
|
380
|
+
import { useState as useState3 } from "react";
|
|
381
|
+
|
|
382
|
+
// src/react/elvix-shield.tsx
|
|
383
|
+
function ElvixShield({
|
|
384
|
+
size,
|
|
385
|
+
fill,
|
|
386
|
+
accent
|
|
387
|
+
}) {
|
|
388
|
+
return /* @__PURE__ */ React.createElement("svg", { width: size, height: size, viewBox: "2 2 20 20", "aria-hidden": true, style: { display: "block" } }, /* @__PURE__ */ React.createElement(
|
|
389
|
+
"path",
|
|
390
|
+
{
|
|
391
|
+
fill,
|
|
392
|
+
fillRule: "evenodd",
|
|
393
|
+
d: "M 6 2.5 C 4.34 2.5 3 3.84 3 5.5 L 3 12.5 C 3 17.5 7 20.7 12 22 C 17 20.7 21 17.5 21 12.5 L 21 5.5 C 21 3.84 19.66 2.5 18 2.5 L 6 2.5 Z M 12 8.4 C 9.79 8.4 8 10.19 8 12.4 C 8 14.61 9.79 16.4 12 16.4 C 13.21 16.4 14.3 15.86 15.04 15 L 13.6 13.77 C 13.21 14.23 12.64 14.5 12 14.5 C 11.04 14.5 10.21 13.86 9.91 13 L 15.95 13 C 15.98 12.8 16 12.6 16 12.4 C 16 10.19 14.21 8.4 12 8.4 Z M 9.91 11.8 L 14.09 11.8 C 13.79 10.94 12.96 10.3 12 10.3 C 11.04 10.3 10.21 10.94 9.91 11.8 Z"
|
|
394
|
+
}
|
|
395
|
+
), /* @__PURE__ */ React.createElement("circle", { cx: "19.5", cy: "4.5", r: "2.4", fill: accent }));
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// src/react/elvix-sign-in-button.tsx
|
|
399
|
+
var ELVIX_URL = "https://elvix.is";
|
|
400
|
+
var PRESET_LABEL = {
|
|
401
|
+
"sign-in-with-elvix": "Sign in with elvix",
|
|
402
|
+
"continue-with-elvix": "Continue with elvix",
|
|
403
|
+
"sign-up-with-elvix": "Sign up with elvix",
|
|
404
|
+
"sign-in": "Sign in",
|
|
405
|
+
"log-in": "Log in",
|
|
406
|
+
continue: "Continue"
|
|
407
|
+
};
|
|
408
|
+
var SIZE_STANDARD = {
|
|
409
|
+
sm: { height: 36, padX: 12, font: 14, gap: 8 },
|
|
410
|
+
md: { height: 40, padX: 12, font: 14, gap: 10 },
|
|
411
|
+
lg: { height: 48, padX: 16, font: 15, gap: 12 }
|
|
412
|
+
};
|
|
413
|
+
var SIZE_ICON = { sm: 36, md: 40, lg: 48 };
|
|
414
|
+
var ICON_SIZE = { sm: 18, md: 20, lg: 22 };
|
|
415
|
+
var RADIUS = { rectangle: 10, pill: 9999, square: 10, circle: 9999 };
|
|
416
|
+
function variantTone(variant, theme) {
|
|
417
|
+
const dark = theme === "dark" || theme === "auto";
|
|
418
|
+
switch (variant) {
|
|
419
|
+
case "filled":
|
|
420
|
+
return { bg: "#6c5ce7", color: "#fff", border: "1px solid rgba(0,0,0,0.1)", shadow: "0 4px 16px -4px rgba(108,92,231,0.45)" };
|
|
421
|
+
case "filled-black":
|
|
422
|
+
return { bg: "#0a0a0b", color: "#fff", border: `1px solid ${dark ? "rgba(255,255,255,0.12)" : "rgba(0,0,0,0.1)"}` };
|
|
423
|
+
case "white":
|
|
424
|
+
return { bg: "#fff", color: "#0a0a0b", border: `1px solid ${dark ? "transparent" : "#e4e4e7"}` };
|
|
425
|
+
case "outline":
|
|
426
|
+
return dark ? { bg: "transparent", color: "#fff", border: "1px solid rgba(142,125,255,0.4)" } : { bg: "#fff", color: "#0a0a0b", border: "1px solid rgba(0,0,0,0.15)" };
|
|
427
|
+
case "ghost":
|
|
428
|
+
return { bg: "transparent", color: dark ? "#fff" : "#0a0a0b", border: "1px solid transparent" };
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
function shieldColor(variant, theme) {
|
|
432
|
+
if (variant === "filled" || variant === "filled-black") return "#ffffff";
|
|
433
|
+
if (variant === "white") return "#0a0a0b";
|
|
434
|
+
return theme === "light" ? "#0a0a0b" : "#ffffff";
|
|
435
|
+
}
|
|
436
|
+
function ElvixSignInButton({
|
|
437
|
+
clientId,
|
|
438
|
+
baseUrl = ELVIX_URL,
|
|
439
|
+
returnUrl,
|
|
440
|
+
type = "standard",
|
|
441
|
+
variant = "filled",
|
|
442
|
+
shape = "rectangle",
|
|
443
|
+
size = "md",
|
|
444
|
+
theme = "dark",
|
|
445
|
+
preset = "sign-in-with-elvix",
|
|
446
|
+
label,
|
|
447
|
+
className,
|
|
448
|
+
href,
|
|
449
|
+
mode = "redirect",
|
|
450
|
+
onClick,
|
|
451
|
+
onResult
|
|
452
|
+
}) {
|
|
453
|
+
const [embedOpen, setEmbedOpen] = useState3(false);
|
|
454
|
+
const isIcon = type === "icon";
|
|
455
|
+
const resolvedLabel = label ?? PRESET_LABEL[preset];
|
|
456
|
+
const tone = variantTone(variant, theme);
|
|
457
|
+
const std = SIZE_STANDARD[size];
|
|
458
|
+
const effectiveShape = isIcon ? shape === "pill" || shape === "circle" ? "circle" : "square" : shape;
|
|
459
|
+
const style = {
|
|
460
|
+
display: "inline-flex",
|
|
461
|
+
alignItems: "center",
|
|
462
|
+
justifyContent: "center",
|
|
463
|
+
fontWeight: 500,
|
|
464
|
+
fontSize: std.font,
|
|
465
|
+
cursor: "pointer",
|
|
466
|
+
userSelect: "none",
|
|
467
|
+
textDecoration: "none",
|
|
468
|
+
borderRadius: RADIUS[effectiveShape],
|
|
469
|
+
background: tone.bg,
|
|
470
|
+
color: tone.color,
|
|
471
|
+
border: tone.border,
|
|
472
|
+
boxShadow: tone.shadow,
|
|
473
|
+
transition: "background 0.15s, border-color 0.15s",
|
|
474
|
+
...isIcon ? { height: SIZE_ICON[size], width: SIZE_ICON[size] } : { height: std.height, paddingLeft: std.padX, paddingRight: std.padX, gap: std.gap }
|
|
475
|
+
};
|
|
476
|
+
const content = /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(ElvixShield, { size: ICON_SIZE[size], fill: shieldColor(variant, theme), accent: "#8e7dff" }), isIcon ? null : /* @__PURE__ */ React.createElement("span", null, resolvedLabel));
|
|
477
|
+
if (mode === "callback") {
|
|
478
|
+
return /* @__PURE__ */ React.createElement("button", { type: "button", onClick, className, style, "aria-label": isIcon ? resolvedLabel : void 0 }, content);
|
|
479
|
+
}
|
|
480
|
+
if (mode === "embed") {
|
|
481
|
+
return /* @__PURE__ */ React.createElement("div", { "data-elvix-signin-button-embed": "" }, !embedOpen && /* @__PURE__ */ React.createElement(
|
|
482
|
+
"button",
|
|
483
|
+
{
|
|
484
|
+
type: "button",
|
|
485
|
+
onClick: () => setEmbedOpen(true),
|
|
486
|
+
className,
|
|
487
|
+
style,
|
|
488
|
+
"aria-label": isIcon ? resolvedLabel : void 0
|
|
489
|
+
},
|
|
490
|
+
content
|
|
491
|
+
), embedOpen && /* @__PURE__ */ React.createElement(
|
|
492
|
+
ElvixSignIn,
|
|
493
|
+
{
|
|
494
|
+
onResult: (r) => {
|
|
495
|
+
onResult?.(r);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
));
|
|
499
|
+
}
|
|
500
|
+
const destination = (() => {
|
|
501
|
+
if (href) return href;
|
|
502
|
+
const base = clientId ? `${baseUrl}/sign-in/${clientId}` : `${baseUrl}/sign-in`;
|
|
503
|
+
if (!returnUrl) return base;
|
|
504
|
+
const sep = base.includes("?") ? "&" : "?";
|
|
505
|
+
return `${base}${sep}return=${encodeURIComponent(returnUrl)}`;
|
|
506
|
+
})();
|
|
507
|
+
return /* @__PURE__ */ React.createElement("a", { href: destination, className, style, "aria-label": isIcon ? resolvedLabel : void 0 }, content);
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// src/react/elvix-secured-badge.tsx
|
|
511
|
+
var ELVIX_URL2 = "https://elvix.is";
|
|
512
|
+
var SIZE = {
|
|
513
|
+
sm: { height: 28, padX: 10, font: 11.5, icon: 14, gap: 6 },
|
|
514
|
+
md: { height: 32, padX: 12, font: 12.5, icon: 16, gap: 7 },
|
|
515
|
+
lg: { height: 36, padX: 14, font: 13, icon: 18, gap: 8 }
|
|
516
|
+
};
|
|
517
|
+
var TONE = {
|
|
518
|
+
white: {
|
|
519
|
+
light: { bg: "#ffffff", border: "#e4e4e7", lead: "#71717a", brand: "#0a0a0b", shield: "#0a0a0b" },
|
|
520
|
+
dark: { bg: "#ffffff", border: "transparent", lead: "#71717a", brand: "#0a0a0b", shield: "#0a0a0b" }
|
|
521
|
+
},
|
|
522
|
+
dark: {
|
|
523
|
+
light: { bg: "#0a0a0b", border: "rgba(0,0,0,0.1)", lead: "#d4d4d8", brand: "#ffffff", shield: "#ffffff" },
|
|
524
|
+
dark: { bg: "#0a0a0b", border: "rgba(255,255,255,0.1)", lead: "#d4d4d8", brand: "#ffffff", shield: "#ffffff" }
|
|
525
|
+
},
|
|
526
|
+
outline: {
|
|
527
|
+
light: { bg: "transparent", border: "rgba(0,0,0,0.15)", lead: "#71717a", brand: "#0a0a0b", shield: "#0a0a0b" },
|
|
528
|
+
dark: { bg: "transparent", border: "rgba(142,125,255,0.4)", lead: "#d4d4d8", brand: "#ffffff", shield: "#ffffff" }
|
|
529
|
+
}
|
|
530
|
+
};
|
|
531
|
+
function ElvixSecuredBadge({
|
|
532
|
+
variant = "white",
|
|
533
|
+
size = "md",
|
|
534
|
+
theme = "dark",
|
|
535
|
+
accentColor = "#8e7dff",
|
|
536
|
+
href = ELVIX_URL2,
|
|
537
|
+
className = ""
|
|
538
|
+
}) {
|
|
539
|
+
const s = SIZE[size];
|
|
540
|
+
const t = TONE[variant][theme];
|
|
541
|
+
const style = {
|
|
542
|
+
display: "inline-flex",
|
|
543
|
+
alignItems: "center",
|
|
544
|
+
gap: s.gap,
|
|
545
|
+
height: s.height,
|
|
546
|
+
paddingLeft: s.padX,
|
|
547
|
+
paddingRight: s.padX,
|
|
548
|
+
fontSize: s.font,
|
|
549
|
+
fontWeight: 500,
|
|
550
|
+
borderRadius: 9999,
|
|
551
|
+
background: t.bg,
|
|
552
|
+
border: `1px solid ${t.border}`,
|
|
553
|
+
color: t.brand,
|
|
554
|
+
textDecoration: "none",
|
|
555
|
+
userSelect: "none",
|
|
556
|
+
lineHeight: 1
|
|
557
|
+
};
|
|
558
|
+
return /* @__PURE__ */ React.createElement(
|
|
559
|
+
"a",
|
|
560
|
+
{
|
|
561
|
+
href,
|
|
562
|
+
target: "_blank",
|
|
563
|
+
rel: "noopener noreferrer",
|
|
564
|
+
className,
|
|
565
|
+
style,
|
|
566
|
+
"data-elvix-secured-badge": ""
|
|
567
|
+
},
|
|
568
|
+
/* @__PURE__ */ React.createElement(ElvixShield, { size: s.icon, fill: t.shield, accent: accentColor }),
|
|
569
|
+
/* @__PURE__ */ React.createElement("span", { style: { display: "inline-flex", alignItems: "baseline", gap: 4 } }, /* @__PURE__ */ React.createElement("span", { style: { color: t.lead } }, "Secured by"), /* @__PURE__ */ React.createElement("span", { style: { color: t.brand, fontWeight: 600 } }, "elvix"))
|
|
570
|
+
);
|
|
330
571
|
}
|
|
331
572
|
|
|
332
573
|
// src/react/hooks.ts
|
|
333
|
-
import { useCallback, useEffect as useEffect2, useState as
|
|
574
|
+
import { useCallback, useEffect as useEffect2, useState as useState4 } from "react";
|
|
334
575
|
var POLL_MS = 7e3;
|
|
335
576
|
function useUserList(kind, opts) {
|
|
336
577
|
const { applicationId, baseUrl = "", pollMs = POLL_MS } = opts;
|
|
337
|
-
const [slugs, setSlugs] =
|
|
338
|
-
const [loading, setLoading] =
|
|
339
|
-
const [error, setError] =
|
|
578
|
+
const [slugs, setSlugs] = useState4([]);
|
|
579
|
+
const [loading, setLoading] = useState4(true);
|
|
580
|
+
const [error, setError] = useState4(null);
|
|
340
581
|
const refresh = useCallback(async () => {
|
|
341
582
|
setError(null);
|
|
342
583
|
try {
|
|
@@ -403,7 +644,7 @@ function ElvixLifecycleWatcher({
|
|
|
403
644
|
}
|
|
404
645
|
|
|
405
646
|
// src/react/elvix-username.tsx
|
|
406
|
-
import { useState as
|
|
647
|
+
import { useState as useState5 } from "react";
|
|
407
648
|
|
|
408
649
|
// src/react/lib.ts
|
|
409
650
|
async function appPost(opts, path, body) {
|
|
@@ -465,10 +706,10 @@ function ElvixUsername({
|
|
|
465
706
|
onResult
|
|
466
707
|
}) {
|
|
467
708
|
const ctx = useElvixContext();
|
|
468
|
-
const [value, setValue] =
|
|
469
|
-
const [busy, setBusy] =
|
|
470
|
-
const [error, setError] =
|
|
471
|
-
const [done, setDone] =
|
|
709
|
+
const [value, setValue] = useState5("");
|
|
710
|
+
const [busy, setBusy] = useState5(false);
|
|
711
|
+
const [error, setError] = useState5(null);
|
|
712
|
+
const [done, setDone] = useState5(null);
|
|
472
713
|
async function submit(e) {
|
|
473
714
|
e.preventDefault();
|
|
474
715
|
if (!ctx.app) return;
|
|
@@ -506,14 +747,14 @@ function ElvixUsername({
|
|
|
506
747
|
}
|
|
507
748
|
|
|
508
749
|
// src/react/elvix-avatar.tsx
|
|
509
|
-
import { useState as
|
|
750
|
+
import { useState as useState6 } from "react";
|
|
510
751
|
function ElvixAvatar({
|
|
511
752
|
onResult
|
|
512
753
|
}) {
|
|
513
754
|
const ctx = useElvixContext();
|
|
514
|
-
const [busy, setBusy] =
|
|
515
|
-
const [error, setError] =
|
|
516
|
-
const [preview, setPreview] =
|
|
755
|
+
const [busy, setBusy] = useState6(false);
|
|
756
|
+
const [error, setError] = useState6(null);
|
|
757
|
+
const [preview, setPreview] = useState6(null);
|
|
517
758
|
async function onFile(e) {
|
|
518
759
|
const file = e.target.files?.[0];
|
|
519
760
|
if (!file || !ctx.app) return;
|
|
@@ -547,14 +788,14 @@ function ElvixAvatar({
|
|
|
547
788
|
}
|
|
548
789
|
|
|
549
790
|
// src/react/elvix-banner.tsx
|
|
550
|
-
import { useState as
|
|
791
|
+
import { useState as useState7 } from "react";
|
|
551
792
|
function ElvixBanner({
|
|
552
793
|
onResult
|
|
553
794
|
}) {
|
|
554
795
|
const ctx = useElvixContext();
|
|
555
|
-
const [busy, setBusy] =
|
|
556
|
-
const [error, setError] =
|
|
557
|
-
const [preview, setPreview] =
|
|
796
|
+
const [busy, setBusy] = useState7(false);
|
|
797
|
+
const [error, setError] = useState7(null);
|
|
798
|
+
const [preview, setPreview] = useState7(null);
|
|
558
799
|
async function onFile(e) {
|
|
559
800
|
const file = e.target.files?.[0];
|
|
560
801
|
if (!file || !ctx.app) return;
|
|
@@ -588,18 +829,18 @@ function ElvixBanner({
|
|
|
588
829
|
}
|
|
589
830
|
|
|
590
831
|
// src/react/elvix-identity-form.tsx
|
|
591
|
-
import { useState as
|
|
832
|
+
import { useState as useState8 } from "react";
|
|
592
833
|
function ElvixIdentityForm({
|
|
593
834
|
initialName = "",
|
|
594
835
|
initialBio = "",
|
|
595
836
|
onResult
|
|
596
837
|
}) {
|
|
597
838
|
const ctx = useElvixContext();
|
|
598
|
-
const [name, setName] =
|
|
599
|
-
const [bio, setBio] =
|
|
600
|
-
const [busy, setBusy] =
|
|
601
|
-
const [error, setError] =
|
|
602
|
-
const [saved, setSaved] =
|
|
839
|
+
const [name, setName] = useState8(initialName);
|
|
840
|
+
const [bio, setBio] = useState8(initialBio);
|
|
841
|
+
const [busy, setBusy] = useState8(false);
|
|
842
|
+
const [error, setError] = useState8(null);
|
|
843
|
+
const [saved, setSaved] = useState8(false);
|
|
603
844
|
async function submit(e) {
|
|
604
845
|
e.preventDefault();
|
|
605
846
|
if (!ctx.app) return;
|
|
@@ -619,18 +860,18 @@ function ElvixIdentityForm({
|
|
|
619
860
|
}
|
|
620
861
|
|
|
621
862
|
// src/react/elvix-region.tsx
|
|
622
|
-
import { useState as
|
|
863
|
+
import { useState as useState9 } from "react";
|
|
623
864
|
function ElvixRegion({
|
|
624
865
|
initialCountry = "",
|
|
625
866
|
initialTimezone = "",
|
|
626
867
|
onResult
|
|
627
868
|
}) {
|
|
628
869
|
const ctx = useElvixContext();
|
|
629
|
-
const [country, setCountry] =
|
|
630
|
-
const [timezone, setTimezone] =
|
|
631
|
-
const [busy, setBusy] =
|
|
632
|
-
const [error, setError] =
|
|
633
|
-
const [saved, setSaved] =
|
|
870
|
+
const [country, setCountry] = useState9(initialCountry);
|
|
871
|
+
const [timezone, setTimezone] = useState9(initialTimezone);
|
|
872
|
+
const [busy, setBusy] = useState9(false);
|
|
873
|
+
const [error, setError] = useState9(null);
|
|
874
|
+
const [saved, setSaved] = useState9(false);
|
|
634
875
|
async function submit(e) {
|
|
635
876
|
e.preventDefault();
|
|
636
877
|
if (!ctx.app) return;
|
|
@@ -650,16 +891,16 @@ function ElvixRegion({
|
|
|
650
891
|
}
|
|
651
892
|
|
|
652
893
|
// src/react/elvix-languages.tsx
|
|
653
|
-
import { useState as
|
|
894
|
+
import { useState as useState10 } from "react";
|
|
654
895
|
function ElvixLanguages({
|
|
655
896
|
initial = [],
|
|
656
897
|
onResult
|
|
657
898
|
}) {
|
|
658
899
|
const ctx = useElvixContext();
|
|
659
|
-
const [raw, setRaw] =
|
|
660
|
-
const [busy, setBusy] =
|
|
661
|
-
const [error, setError] =
|
|
662
|
-
const [saved, setSaved] =
|
|
900
|
+
const [raw, setRaw] = useState10(initial.join(", "));
|
|
901
|
+
const [busy, setBusy] = useState10(false);
|
|
902
|
+
const [error, setError] = useState10(null);
|
|
903
|
+
const [saved, setSaved] = useState10(false);
|
|
663
904
|
async function submit(e) {
|
|
664
905
|
e.preventDefault();
|
|
665
906
|
if (!ctx.app) return;
|
|
@@ -680,14 +921,14 @@ function ElvixLanguages({
|
|
|
680
921
|
}
|
|
681
922
|
|
|
682
923
|
// src/react/elvix-sessions.tsx
|
|
683
|
-
import { useEffect as useEffect4, useState as
|
|
924
|
+
import { useEffect as useEffect4, useState as useState11 } from "react";
|
|
684
925
|
function ElvixSessions({
|
|
685
926
|
onResult
|
|
686
927
|
}) {
|
|
687
928
|
const ctx = useElvixContext();
|
|
688
|
-
const [rows, setRows] =
|
|
689
|
-
const [error, setError] =
|
|
690
|
-
const [busy, setBusy] =
|
|
929
|
+
const [rows, setRows] = useState11(null);
|
|
930
|
+
const [error, setError] = useState11(null);
|
|
931
|
+
const [busy, setBusy] = useState11(false);
|
|
691
932
|
useEffect4(() => {
|
|
692
933
|
if (!ctx.app) return;
|
|
693
934
|
fetch(`${ctx.baseUrl}/api/account/apps/${ctx.app.applicationId}/sessions`, {
|
|
@@ -712,14 +953,14 @@ function ElvixSessions({
|
|
|
712
953
|
}
|
|
713
954
|
|
|
714
955
|
// src/react/elvix-export.tsx
|
|
715
|
-
import { useState as
|
|
956
|
+
import { useState as useState12 } from "react";
|
|
716
957
|
function ElvixExport({
|
|
717
958
|
onResult
|
|
718
959
|
}) {
|
|
719
960
|
const ctx = useElvixContext();
|
|
720
|
-
const [busy, setBusy] =
|
|
721
|
-
const [done, setDone] =
|
|
722
|
-
const [error, setError] =
|
|
961
|
+
const [busy, setBusy] = useState12(false);
|
|
962
|
+
const [done, setDone] = useState12(false);
|
|
963
|
+
const [error, setError] = useState12(null);
|
|
723
964
|
async function start() {
|
|
724
965
|
if (!ctx.app) return;
|
|
725
966
|
setBusy(true);
|
|
@@ -738,16 +979,16 @@ function ElvixExport({
|
|
|
738
979
|
}
|
|
739
980
|
|
|
740
981
|
// src/react/elvix-deactivate.tsx
|
|
741
|
-
import { useState as
|
|
982
|
+
import { useState as useState13 } from "react";
|
|
742
983
|
function ElvixDeactivate({
|
|
743
984
|
onResult
|
|
744
985
|
}) {
|
|
745
986
|
const ctx = useElvixContext();
|
|
746
|
-
const [pane, setPane] =
|
|
747
|
-
const [challengeId, setChallengeId] =
|
|
748
|
-
const [code, setCode] =
|
|
749
|
-
const [busy, setBusy] =
|
|
750
|
-
const [error, setError] =
|
|
987
|
+
const [pane, setPane] = useState13("warn");
|
|
988
|
+
const [challengeId, setChallengeId] = useState13(null);
|
|
989
|
+
const [code, setCode] = useState13("");
|
|
990
|
+
const [busy, setBusy] = useState13(false);
|
|
991
|
+
const [error, setError] = useState13(null);
|
|
751
992
|
async function startChallenge() {
|
|
752
993
|
if (!ctx.app) return;
|
|
753
994
|
setBusy(true);
|
|
@@ -804,16 +1045,16 @@ function ElvixDeactivate({
|
|
|
804
1045
|
}
|
|
805
1046
|
|
|
806
1047
|
// src/react/elvix-leave.tsx
|
|
807
|
-
import { useState as
|
|
1048
|
+
import { useState as useState14 } from "react";
|
|
808
1049
|
function ElvixLeave({
|
|
809
1050
|
onResult
|
|
810
1051
|
}) {
|
|
811
1052
|
const ctx = useElvixContext();
|
|
812
|
-
const [pane, setPane] =
|
|
813
|
-
const [challengeId, setChallengeId] =
|
|
814
|
-
const [code, setCode] =
|
|
815
|
-
const [busy, setBusy] =
|
|
816
|
-
const [error, setError] =
|
|
1053
|
+
const [pane, setPane] = useState14("warn");
|
|
1054
|
+
const [challengeId, setChallengeId] = useState14(null);
|
|
1055
|
+
const [code, setCode] = useState14("");
|
|
1056
|
+
const [busy, setBusy] = useState14(false);
|
|
1057
|
+
const [error, setError] = useState14(null);
|
|
817
1058
|
async function startChallenge() {
|
|
818
1059
|
if (!ctx.app) return;
|
|
819
1060
|
setBusy(true);
|
|
@@ -870,16 +1111,16 @@ function ElvixLeave({
|
|
|
870
1111
|
}
|
|
871
1112
|
|
|
872
1113
|
// src/react/elvix-address-book.tsx
|
|
873
|
-
import { useEffect as useEffect5, useState as
|
|
1114
|
+
import { useEffect as useEffect5, useState as useState15 } from "react";
|
|
874
1115
|
function ElvixAddressBook({
|
|
875
1116
|
onResult
|
|
876
1117
|
}) {
|
|
877
1118
|
const ctx = useElvixContext();
|
|
878
|
-
const [rows, setRows] =
|
|
879
|
-
const [error, setError] =
|
|
880
|
-
const [busy, setBusy] =
|
|
881
|
-
const [adding, setAdding] =
|
|
882
|
-
const [form, setForm] =
|
|
1119
|
+
const [rows, setRows] = useState15(null);
|
|
1120
|
+
const [error, setError] = useState15(null);
|
|
1121
|
+
const [busy, setBusy] = useState15(false);
|
|
1122
|
+
const [adding, setAdding] = useState15(false);
|
|
1123
|
+
const [form, setForm] = useState15({
|
|
883
1124
|
label: "Home",
|
|
884
1125
|
line1: "",
|
|
885
1126
|
postalCode: "",
|
|
@@ -932,16 +1173,16 @@ function ElvixAddressBook({
|
|
|
932
1173
|
}
|
|
933
1174
|
|
|
934
1175
|
// src/react/elvix-legal-entities.tsx
|
|
935
|
-
import { useEffect as useEffect6, useState as
|
|
1176
|
+
import { useEffect as useEffect6, useState as useState16 } from "react";
|
|
936
1177
|
function ElvixLegalEntities({
|
|
937
1178
|
onResult
|
|
938
1179
|
}) {
|
|
939
1180
|
const ctx = useElvixContext();
|
|
940
|
-
const [rows, setRows] =
|
|
941
|
-
const [error, setError] =
|
|
942
|
-
const [busy, setBusy] =
|
|
943
|
-
const [adding, setAdding] =
|
|
944
|
-
const [form, setForm] =
|
|
1181
|
+
const [rows, setRows] = useState16(null);
|
|
1182
|
+
const [error, setError] = useState16(null);
|
|
1183
|
+
const [busy, setBusy] = useState16(false);
|
|
1184
|
+
const [adding, setAdding] = useState16(false);
|
|
1185
|
+
const [form, setForm] = useState16({
|
|
945
1186
|
legalName: "",
|
|
946
1187
|
taxId: "",
|
|
947
1188
|
country: ""
|
|
@@ -991,6 +1232,7 @@ function ElvixLegalEntities({
|
|
|
991
1232
|
return /* @__PURE__ */ React.createElement(ElvixCard, { title: "Legal entities" }, error && /* @__PURE__ */ React.createElement("p", { role: "alert", className: "elvix-error" }, error), !rows && !error && /* @__PURE__ */ React.createElement("p", null, "Loading\u2026"), rows && rows.length === 0 && /* @__PURE__ */ React.createElement("p", { className: "elvix-muted" }, "No legal entities yet."), rows?.map((e) => /* @__PURE__ */ React.createElement("div", { key: e.id, style: { padding: "8px 0", borderBottom: "1px solid rgba(0,0,0,0.06)", display: "flex", justifyContent: "space-between", gap: 12 } }, /* @__PURE__ */ React.createElement("div", { style: { fontSize: 13 } }, /* @__PURE__ */ React.createElement("div", { style: { fontWeight: 500 } }, e.legalName), /* @__PURE__ */ React.createElement("div", { style: { color: "rgba(0,0,0,0.55)" } }, e.taxId, " \xB7 ", e.country)), /* @__PURE__ */ React.createElement("button", { type: "button", disabled: busy, onClick: () => remove(e.id), className: "elvix-btn elvix-btn-ghost" }, "Remove"))), !adding && /* @__PURE__ */ React.createElement("button", { type: "button", onClick: () => setAdding(true), className: "elvix-btn elvix-btn-primary", style: { marginTop: 12 } }, "Add entity"), adding && /* @__PURE__ */ React.createElement("form", { onSubmit: add, className: "elvix-form", style: { marginTop: 12 } }, /* @__PURE__ */ React.createElement("input", { value: form.legalName, onChange: (e) => setForm({ ...form, legalName: e.target.value }), placeholder: "Legal name", required: true, className: "elvix-input" }), /* @__PURE__ */ React.createElement("input", { value: form.taxId, onChange: (e) => setForm({ ...form, taxId: e.target.value }), placeholder: "Tax / VAT ID", required: true, className: "elvix-input" }), /* @__PURE__ */ React.createElement("input", { value: form.country, onChange: (e) => setForm({ ...form, country: e.target.value.toUpperCase() }), placeholder: "Country (ISO-2)", maxLength: 2, required: true, className: "elvix-input" }), /* @__PURE__ */ React.createElement("button", { type: "submit", disabled: busy, className: "elvix-btn elvix-btn-primary" }, busy ? "Saving\u2026" : "Save")));
|
|
992
1233
|
}
|
|
993
1234
|
export {
|
|
1235
|
+
DEFAULT_COPY,
|
|
994
1236
|
ElvixAddressBook,
|
|
995
1237
|
ElvixAvatar,
|
|
996
1238
|
ElvixBanner,
|
|
@@ -1004,8 +1246,10 @@ export {
|
|
|
1004
1246
|
ElvixLifecycleWatcher,
|
|
1005
1247
|
ElvixProvider,
|
|
1006
1248
|
ElvixRegion,
|
|
1249
|
+
ElvixSecuredBadge,
|
|
1007
1250
|
ElvixSessions,
|
|
1008
1251
|
ElvixSignIn,
|
|
1252
|
+
ElvixSignInButton,
|
|
1009
1253
|
ElvixUsername,
|
|
1010
1254
|
getElvixToken,
|
|
1011
1255
|
setElvixToken,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elvix.is/sdk",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.3",
|
|
4
4
|
"description": "Official elvix SDK. Drop-in React components, server helpers, and an MCP server so AI coding agents integrate elvix on the first try.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://elvix.is",
|