@elvix.is/sdk 0.5.0 → 0.5.2
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 +106 -2
- package/dist/react.js +327 -80
- 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;
|
|
@@ -60,6 +108,12 @@ type ElvixBootstrapEnvelope = {
|
|
|
60
108
|
};
|
|
61
109
|
signInVerb: "signin" | "login";
|
|
62
110
|
signinGate: "public" | "private_beta" | "closed";
|
|
111
|
+
/**
|
|
112
|
+
* Console-configured sign-in copy overrides. Any subset of the strings the
|
|
113
|
+
* sign-in surface renders; missing keys fall back to the built-in English
|
|
114
|
+
* defaults. A `copy` prop on the component overrides these in turn.
|
|
115
|
+
*/
|
|
116
|
+
strings?: Partial<ElvixCopy>;
|
|
63
117
|
};
|
|
64
118
|
type ElvixSignInResultOk = {
|
|
65
119
|
ok: true;
|
|
@@ -119,13 +173,63 @@ declare function ElvixProvider({ clientId, theme, brand, baseUrl, children, clas
|
|
|
119
173
|
* exposes. Hosts navigate from the callback; this component never
|
|
120
174
|
* calls `router.push` itself.
|
|
121
175
|
*/
|
|
122
|
-
declare function ElvixSignIn({ onResult, redirectAfterSignIn, className, }: {
|
|
176
|
+
declare function ElvixSignIn({ onResult, redirectAfterSignIn, copy: copyProp, className, }: {
|
|
123
177
|
onResult?: (r: ElvixSignInResult) => void;
|
|
124
178
|
/** Default redirect target on success when the server doesn't echo one. */
|
|
125
179
|
redirectAfterSignIn?: string;
|
|
180
|
+
/**
|
|
181
|
+
* Thin per-embed copy override. The primary way to edit copy is the elvix
|
|
182
|
+
* Console (served live in the bootstrap `strings`); this prop just lets a
|
|
183
|
+
* single embed tweak a string or two without a Console change.
|
|
184
|
+
*/
|
|
185
|
+
copy?: Partial<ElvixCopy>;
|
|
126
186
|
className?: string;
|
|
127
187
|
}): react.JSX.Element;
|
|
128
188
|
|
|
189
|
+
type ElvixSignInButtonSize = "sm" | "md" | "lg";
|
|
190
|
+
type ElvixSignInButtonTheme = "light" | "dark" | "auto";
|
|
191
|
+
type ElvixSignInButtonVariant = "filled" | "filled-black" | "white" | "outline" | "ghost";
|
|
192
|
+
type ElvixSignInButtonShape = "rectangle" | "pill" | "square" | "circle";
|
|
193
|
+
type ElvixSignInButtonType = "standard" | "icon";
|
|
194
|
+
type ElvixSignInButtonMode = "redirect" | "callback" | "embed";
|
|
195
|
+
type ElvixSignInPreset = "sign-in-with-elvix" | "continue-with-elvix" | "sign-up-with-elvix" | "sign-in" | "log-in" | "continue";
|
|
196
|
+
type ElvixSignInButtonProps = {
|
|
197
|
+
clientId?: string;
|
|
198
|
+
/** elvix origin for redirect mode. Defaults to https://elvix.is. */
|
|
199
|
+
baseUrl?: string;
|
|
200
|
+
returnUrl?: string;
|
|
201
|
+
type?: ElvixSignInButtonType;
|
|
202
|
+
variant?: ElvixSignInButtonVariant;
|
|
203
|
+
shape?: ElvixSignInButtonShape;
|
|
204
|
+
size?: ElvixSignInButtonSize;
|
|
205
|
+
theme?: ElvixSignInButtonTheme;
|
|
206
|
+
preset?: ElvixSignInPreset;
|
|
207
|
+
label?: string;
|
|
208
|
+
className?: string;
|
|
209
|
+
href?: string;
|
|
210
|
+
mode?: ElvixSignInButtonMode;
|
|
211
|
+
onClick?: () => void;
|
|
212
|
+
/** Terminal outcome of mode="embed": success (with token) or error. */
|
|
213
|
+
onResult?: (result: ElvixSignInResult) => void;
|
|
214
|
+
};
|
|
215
|
+
declare function ElvixSignInButton({ clientId, baseUrl, returnUrl, type, variant, shape, size, theme, preset, label, className, href, mode, onClick, onResult, }: ElvixSignInButtonProps): react.JSX.Element;
|
|
216
|
+
|
|
217
|
+
type ElvixSecuredBadgeVariant = "white" | "dark" | "outline";
|
|
218
|
+
type ElvixSecuredBadgeSize = "sm" | "md" | "lg";
|
|
219
|
+
type ElvixSecuredBadgeTheme = "light" | "dark";
|
|
220
|
+
type ElvixSecuredBadgeProps = {
|
|
221
|
+
variant?: ElvixSecuredBadgeVariant;
|
|
222
|
+
size?: ElvixSecuredBadgeSize;
|
|
223
|
+
/** Which side of the colour wheel the host page is on (for the outline variant). */
|
|
224
|
+
theme?: ElvixSecuredBadgeTheme;
|
|
225
|
+
/** Active-protection dot colour. Defaults to brand lavender. */
|
|
226
|
+
accentColor?: string;
|
|
227
|
+
/** Where the badge links. Defaults to elvix. */
|
|
228
|
+
href?: string;
|
|
229
|
+
className?: string;
|
|
230
|
+
};
|
|
231
|
+
declare function ElvixSecuredBadge({ variant, size, theme, accentColor, href, className, }: ElvixSecuredBadgeProps): react.JSX.Element;
|
|
232
|
+
|
|
129
233
|
/**
|
|
130
234
|
* Session token store for cross-origin SDK use.
|
|
131
235
|
*
|
|
@@ -280,4 +384,4 @@ declare function ElvixLegalEntities({ onResult, }: {
|
|
|
280
384
|
onResult?: (r: ElvixActionResult) => void;
|
|
281
385
|
}): react.JSX.Element;
|
|
282
386
|
|
|
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 };
|
|
387
|
+
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
|
@@ -167,6 +167,42 @@ function withAlpha(hex, a) {
|
|
|
167
167
|
// src/react/elvix-sign-in.tsx
|
|
168
168
|
import { useState as useState2 } from "react";
|
|
169
169
|
|
|
170
|
+
// src/react/copy.ts
|
|
171
|
+
var DEFAULT_COPY = {
|
|
172
|
+
subtitle: "Pick how you want to continue.",
|
|
173
|
+
googleButton: "Continue with Google",
|
|
174
|
+
emailPlaceholder: "you@example.com",
|
|
175
|
+
sendCodeButton: "Send code",
|
|
176
|
+
sendingLabel: "Sending\u2026",
|
|
177
|
+
codeSentSubtitle: "We sent a 6-digit code to {email}.",
|
|
178
|
+
codePlaceholder: "123456",
|
|
179
|
+
verifyingLabel: "Verifying\u2026",
|
|
180
|
+
signedInText: "Signed in.",
|
|
181
|
+
errorEnterEmail: "Enter an email.",
|
|
182
|
+
errorEnterCode: "Enter the 6-digit code."
|
|
183
|
+
};
|
|
184
|
+
function resolveCopy(bootstrap, prop) {
|
|
185
|
+
return {
|
|
186
|
+
...DEFAULT_COPY,
|
|
187
|
+
...stripUndefined(bootstrap),
|
|
188
|
+
...stripUndefined(prop)
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
function fillCopy(template, tokens) {
|
|
192
|
+
return template.replace(
|
|
193
|
+
/\{(\w+)\}/g,
|
|
194
|
+
(whole, key) => key in tokens ? tokens[key] : whole
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
function stripUndefined(o) {
|
|
198
|
+
if (!o) return {};
|
|
199
|
+
const out = {};
|
|
200
|
+
for (const [k, v] of Object.entries(o)) {
|
|
201
|
+
if (v !== void 0) out[k] = v;
|
|
202
|
+
}
|
|
203
|
+
return out;
|
|
204
|
+
}
|
|
205
|
+
|
|
170
206
|
// src/react/session.ts
|
|
171
207
|
var STORAGE_KEY = "elvix.session.token";
|
|
172
208
|
var memToken = null;
|
|
@@ -193,15 +229,26 @@ function authInit() {
|
|
|
193
229
|
const token = getElvixToken();
|
|
194
230
|
return token ? { headers: { authorization: `Bearer ${token}` }, credentials: "omit" } : { headers: {}, credentials: "include" };
|
|
195
231
|
}
|
|
232
|
+
function isSameOrigin(baseUrl) {
|
|
233
|
+
if (!baseUrl) return true;
|
|
234
|
+
if (typeof window === "undefined") return true;
|
|
235
|
+
try {
|
|
236
|
+
return new URL(baseUrl, window.location.href).origin === window.location.origin;
|
|
237
|
+
} catch {
|
|
238
|
+
return true;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
196
241
|
|
|
197
242
|
// src/react/elvix-sign-in.tsx
|
|
198
243
|
function ElvixSignIn({
|
|
199
244
|
onResult,
|
|
200
245
|
redirectAfterSignIn,
|
|
246
|
+
copy: copyProp,
|
|
201
247
|
className = ""
|
|
202
248
|
}) {
|
|
203
249
|
const ctx = useElvixContext();
|
|
204
250
|
const app = useElvixApp();
|
|
251
|
+
const copy = resolveCopy(app?.strings, copyProp);
|
|
205
252
|
const [step, setStep] = useState2("identify");
|
|
206
253
|
const [email, setEmail] = useState2("");
|
|
207
254
|
const [code, setCode] = useState2("");
|
|
@@ -209,6 +256,9 @@ function ElvixSignIn({
|
|
|
209
256
|
const [busy, setBusy] = useState2(false);
|
|
210
257
|
const [error, setError] = useState2(null);
|
|
211
258
|
const verb = app?.signInVerb === "login" ? "Log in" : "Sign in";
|
|
259
|
+
const defaultTitle = app?.appName ? `${verb} to ${app.appName}` : verb;
|
|
260
|
+
const title = copy.title ? fillCopy(copy.title, { app: app?.appName ?? "" }) : defaultTitle;
|
|
261
|
+
const submitLabel = copy.submitButton ?? verb;
|
|
212
262
|
function fail(error2, message) {
|
|
213
263
|
setError(message ?? error2);
|
|
214
264
|
onResult?.({ ok: false, error: error2, message });
|
|
@@ -221,14 +271,14 @@ function ElvixSignIn({
|
|
|
221
271
|
}
|
|
222
272
|
async function startOtp(e) {
|
|
223
273
|
e.preventDefault();
|
|
224
|
-
if (!email.trim()) return fail("invalid_input",
|
|
274
|
+
if (!email.trim()) return fail("invalid_input", copy.errorEnterEmail);
|
|
225
275
|
setBusy(true);
|
|
226
276
|
setError(null);
|
|
227
277
|
try {
|
|
228
278
|
const res = await fetch(`${ctx.baseUrl}/api/auth/otp/start`, {
|
|
229
279
|
method: "POST",
|
|
230
280
|
headers: { "content-type": "application/json" },
|
|
231
|
-
credentials: "include",
|
|
281
|
+
credentials: isSameOrigin(ctx.baseUrl) ? "include" : "omit",
|
|
232
282
|
body: JSON.stringify({
|
|
233
283
|
email: email.trim(),
|
|
234
284
|
intent: "app",
|
|
@@ -250,14 +300,14 @@ function ElvixSignIn({
|
|
|
250
300
|
async function verifyOtp(e) {
|
|
251
301
|
e.preventDefault();
|
|
252
302
|
if (!challengeId) return;
|
|
253
|
-
if (code.trim().length !== 6) return fail("invalid_input",
|
|
303
|
+
if (code.trim().length !== 6) return fail("invalid_input", copy.errorEnterCode);
|
|
254
304
|
setBusy(true);
|
|
255
305
|
setError(null);
|
|
256
306
|
try {
|
|
257
307
|
const res = await fetch(`${ctx.baseUrl}/api/auth/otp/verify`, {
|
|
258
308
|
method: "POST",
|
|
259
309
|
headers: { "content-type": "application/json" },
|
|
260
|
-
credentials: "include",
|
|
310
|
+
credentials: isSameOrigin(ctx.baseUrl) ? "include" : "omit",
|
|
261
311
|
body: JSON.stringify({ challengeId, code: code.trim() })
|
|
262
312
|
});
|
|
263
313
|
const body = await res.json();
|
|
@@ -280,9 +330,9 @@ function ElvixSignIn({
|
|
|
280
330
|
}
|
|
281
331
|
const card = `elvix-card ${className}`.trim();
|
|
282
332
|
if (step === "done") {
|
|
283
|
-
return /* @__PURE__ */ React.createElement("div", { className: card, "data-elvix-pane": "done" }, /* @__PURE__ */ React.createElement("p", null,
|
|
333
|
+
return /* @__PURE__ */ React.createElement("div", { className: card, "data-elvix-pane": "done" }, /* @__PURE__ */ React.createElement("p", null, copy.signedInText));
|
|
284
334
|
}
|
|
285
|
-
return /* @__PURE__ */ React.createElement("div", { className: card, "data-elvix-pane": step }, /* @__PURE__ */ React.createElement("h2", { className: "elvix-h" },
|
|
335
|
+
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?.methods.google && /* @__PURE__ */ React.createElement(
|
|
286
336
|
"button",
|
|
287
337
|
{
|
|
288
338
|
type: "button",
|
|
@@ -291,19 +341,19 @@ function ElvixSignIn({
|
|
|
291
341
|
className: "elvix-btn elvix-btn-google",
|
|
292
342
|
"data-elvix-method": "google"
|
|
293
343
|
},
|
|
294
|
-
|
|
344
|
+
copy.googleButton
|
|
295
345
|
), app?.methods.emailOtp && /* @__PURE__ */ React.createElement("form", { onSubmit: startOtp, "data-elvix-method": "email_otp", className: "elvix-otp-form" }, /* @__PURE__ */ React.createElement(
|
|
296
346
|
"input",
|
|
297
347
|
{
|
|
298
348
|
type: "email",
|
|
299
349
|
value: email,
|
|
300
350
|
onChange: (ev) => setEmail(ev.target.value),
|
|
301
|
-
placeholder:
|
|
351
|
+
placeholder: copy.emailPlaceholder,
|
|
302
352
|
required: true,
|
|
303
353
|
disabled: busy,
|
|
304
354
|
className: "elvix-input"
|
|
305
355
|
}
|
|
306
|
-
), /* @__PURE__ */ React.createElement("button", { type: "submit", disabled: busy, className: "elvix-btn elvix-btn-primary" }, busy ?
|
|
356
|
+
), /* @__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(
|
|
307
357
|
"input",
|
|
308
358
|
{
|
|
309
359
|
type: "text",
|
|
@@ -312,22 +362,216 @@ function ElvixSignIn({
|
|
|
312
362
|
maxLength: 6,
|
|
313
363
|
value: code,
|
|
314
364
|
onChange: (ev) => setCode(ev.target.value.replace(/\D/g, "")),
|
|
315
|
-
placeholder:
|
|
365
|
+
placeholder: copy.codePlaceholder,
|
|
316
366
|
required: true,
|
|
317
367
|
disabled: busy,
|
|
318
368
|
className: "elvix-input"
|
|
319
369
|
}
|
|
320
|
-
), /* @__PURE__ */ React.createElement("button", { type: "submit", disabled: busy, className: "elvix-btn elvix-btn-primary" }, busy ?
|
|
370
|
+
), /* @__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));
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// src/react/elvix-sign-in-button.tsx
|
|
374
|
+
import { useState as useState3 } from "react";
|
|
375
|
+
|
|
376
|
+
// src/react/elvix-shield.tsx
|
|
377
|
+
function ElvixShield({
|
|
378
|
+
size,
|
|
379
|
+
fill,
|
|
380
|
+
accent
|
|
381
|
+
}) {
|
|
382
|
+
return /* @__PURE__ */ React.createElement("svg", { width: size, height: size, viewBox: "2 2 20 20", "aria-hidden": true, style: { display: "block" } }, /* @__PURE__ */ React.createElement(
|
|
383
|
+
"path",
|
|
384
|
+
{
|
|
385
|
+
fill,
|
|
386
|
+
fillRule: "evenodd",
|
|
387
|
+
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"
|
|
388
|
+
}
|
|
389
|
+
), /* @__PURE__ */ React.createElement("circle", { cx: "19.5", cy: "4.5", r: "2.4", fill: accent }));
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// src/react/elvix-sign-in-button.tsx
|
|
393
|
+
var ELVIX_URL = "https://elvix.is";
|
|
394
|
+
var PRESET_LABEL = {
|
|
395
|
+
"sign-in-with-elvix": "Sign in with elvix",
|
|
396
|
+
"continue-with-elvix": "Continue with elvix",
|
|
397
|
+
"sign-up-with-elvix": "Sign up with elvix",
|
|
398
|
+
"sign-in": "Sign in",
|
|
399
|
+
"log-in": "Log in",
|
|
400
|
+
continue: "Continue"
|
|
401
|
+
};
|
|
402
|
+
var SIZE_STANDARD = {
|
|
403
|
+
sm: { height: 36, padX: 12, font: 14, gap: 8 },
|
|
404
|
+
md: { height: 40, padX: 12, font: 14, gap: 10 },
|
|
405
|
+
lg: { height: 48, padX: 16, font: 15, gap: 12 }
|
|
406
|
+
};
|
|
407
|
+
var SIZE_ICON = { sm: 36, md: 40, lg: 48 };
|
|
408
|
+
var ICON_SIZE = { sm: 18, md: 20, lg: 22 };
|
|
409
|
+
var RADIUS = { rectangle: 10, pill: 9999, square: 10, circle: 9999 };
|
|
410
|
+
function variantTone(variant, theme) {
|
|
411
|
+
const dark = theme === "dark" || theme === "auto";
|
|
412
|
+
switch (variant) {
|
|
413
|
+
case "filled":
|
|
414
|
+
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)" };
|
|
415
|
+
case "filled-black":
|
|
416
|
+
return { bg: "#0a0a0b", color: "#fff", border: `1px solid ${dark ? "rgba(255,255,255,0.12)" : "rgba(0,0,0,0.1)"}` };
|
|
417
|
+
case "white":
|
|
418
|
+
return { bg: "#fff", color: "#0a0a0b", border: `1px solid ${dark ? "transparent" : "#e4e4e7"}` };
|
|
419
|
+
case "outline":
|
|
420
|
+
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)" };
|
|
421
|
+
case "ghost":
|
|
422
|
+
return { bg: "transparent", color: dark ? "#fff" : "#0a0a0b", border: "1px solid transparent" };
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
function shieldColor(variant, theme) {
|
|
426
|
+
if (variant === "filled" || variant === "filled-black") return "#ffffff";
|
|
427
|
+
if (variant === "white") return "#0a0a0b";
|
|
428
|
+
return theme === "light" ? "#0a0a0b" : "#ffffff";
|
|
429
|
+
}
|
|
430
|
+
function ElvixSignInButton({
|
|
431
|
+
clientId,
|
|
432
|
+
baseUrl = ELVIX_URL,
|
|
433
|
+
returnUrl,
|
|
434
|
+
type = "standard",
|
|
435
|
+
variant = "filled",
|
|
436
|
+
shape = "rectangle",
|
|
437
|
+
size = "md",
|
|
438
|
+
theme = "dark",
|
|
439
|
+
preset = "sign-in-with-elvix",
|
|
440
|
+
label,
|
|
441
|
+
className,
|
|
442
|
+
href,
|
|
443
|
+
mode = "redirect",
|
|
444
|
+
onClick,
|
|
445
|
+
onResult
|
|
446
|
+
}) {
|
|
447
|
+
const [embedOpen, setEmbedOpen] = useState3(false);
|
|
448
|
+
const isIcon = type === "icon";
|
|
449
|
+
const resolvedLabel = label ?? PRESET_LABEL[preset];
|
|
450
|
+
const tone = variantTone(variant, theme);
|
|
451
|
+
const std = SIZE_STANDARD[size];
|
|
452
|
+
const effectiveShape = isIcon ? shape === "pill" || shape === "circle" ? "circle" : "square" : shape;
|
|
453
|
+
const style = {
|
|
454
|
+
display: "inline-flex",
|
|
455
|
+
alignItems: "center",
|
|
456
|
+
justifyContent: "center",
|
|
457
|
+
fontWeight: 500,
|
|
458
|
+
fontSize: std.font,
|
|
459
|
+
cursor: "pointer",
|
|
460
|
+
userSelect: "none",
|
|
461
|
+
textDecoration: "none",
|
|
462
|
+
borderRadius: RADIUS[effectiveShape],
|
|
463
|
+
background: tone.bg,
|
|
464
|
+
color: tone.color,
|
|
465
|
+
border: tone.border,
|
|
466
|
+
boxShadow: tone.shadow,
|
|
467
|
+
transition: "background 0.15s, border-color 0.15s",
|
|
468
|
+
...isIcon ? { height: SIZE_ICON[size], width: SIZE_ICON[size] } : { height: std.height, paddingLeft: std.padX, paddingRight: std.padX, gap: std.gap }
|
|
469
|
+
};
|
|
470
|
+
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));
|
|
471
|
+
if (mode === "callback") {
|
|
472
|
+
return /* @__PURE__ */ React.createElement("button", { type: "button", onClick, className, style, "aria-label": isIcon ? resolvedLabel : void 0 }, content);
|
|
473
|
+
}
|
|
474
|
+
if (mode === "embed") {
|
|
475
|
+
return /* @__PURE__ */ React.createElement("div", { "data-elvix-signin-button-embed": "" }, !embedOpen && /* @__PURE__ */ React.createElement(
|
|
476
|
+
"button",
|
|
477
|
+
{
|
|
478
|
+
type: "button",
|
|
479
|
+
onClick: () => setEmbedOpen(true),
|
|
480
|
+
className,
|
|
481
|
+
style,
|
|
482
|
+
"aria-label": isIcon ? resolvedLabel : void 0
|
|
483
|
+
},
|
|
484
|
+
content
|
|
485
|
+
), embedOpen && /* @__PURE__ */ React.createElement(
|
|
486
|
+
ElvixSignIn,
|
|
487
|
+
{
|
|
488
|
+
onResult: (r) => {
|
|
489
|
+
onResult?.(r);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
));
|
|
493
|
+
}
|
|
494
|
+
const destination = (() => {
|
|
495
|
+
if (href) return href;
|
|
496
|
+
const base = clientId ? `${baseUrl}/sign-in/${clientId}` : `${baseUrl}/sign-in`;
|
|
497
|
+
if (!returnUrl) return base;
|
|
498
|
+
const sep = base.includes("?") ? "&" : "?";
|
|
499
|
+
return `${base}${sep}return=${encodeURIComponent(returnUrl)}`;
|
|
500
|
+
})();
|
|
501
|
+
return /* @__PURE__ */ React.createElement("a", { href: destination, className, style, "aria-label": isIcon ? resolvedLabel : void 0 }, content);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
// src/react/elvix-secured-badge.tsx
|
|
505
|
+
var ELVIX_URL2 = "https://elvix.is";
|
|
506
|
+
var SIZE = {
|
|
507
|
+
sm: { height: 28, padX: 10, font: 11.5, icon: 14, gap: 6 },
|
|
508
|
+
md: { height: 32, padX: 12, font: 12.5, icon: 16, gap: 7 },
|
|
509
|
+
lg: { height: 36, padX: 14, font: 13, icon: 18, gap: 8 }
|
|
510
|
+
};
|
|
511
|
+
var TONE = {
|
|
512
|
+
white: {
|
|
513
|
+
light: { bg: "#ffffff", border: "#e4e4e7", lead: "#71717a", brand: "#0a0a0b", shield: "#0a0a0b" },
|
|
514
|
+
dark: { bg: "#ffffff", border: "transparent", lead: "#71717a", brand: "#0a0a0b", shield: "#0a0a0b" }
|
|
515
|
+
},
|
|
516
|
+
dark: {
|
|
517
|
+
light: { bg: "#0a0a0b", border: "rgba(0,0,0,0.1)", lead: "#d4d4d8", brand: "#ffffff", shield: "#ffffff" },
|
|
518
|
+
dark: { bg: "#0a0a0b", border: "rgba(255,255,255,0.1)", lead: "#d4d4d8", brand: "#ffffff", shield: "#ffffff" }
|
|
519
|
+
},
|
|
520
|
+
outline: {
|
|
521
|
+
light: { bg: "transparent", border: "rgba(0,0,0,0.15)", lead: "#71717a", brand: "#0a0a0b", shield: "#0a0a0b" },
|
|
522
|
+
dark: { bg: "transparent", border: "rgba(142,125,255,0.4)", lead: "#d4d4d8", brand: "#ffffff", shield: "#ffffff" }
|
|
523
|
+
}
|
|
524
|
+
};
|
|
525
|
+
function ElvixSecuredBadge({
|
|
526
|
+
variant = "white",
|
|
527
|
+
size = "md",
|
|
528
|
+
theme = "dark",
|
|
529
|
+
accentColor = "#8e7dff",
|
|
530
|
+
href = ELVIX_URL2,
|
|
531
|
+
className = ""
|
|
532
|
+
}) {
|
|
533
|
+
const s = SIZE[size];
|
|
534
|
+
const t = TONE[variant][theme];
|
|
535
|
+
const style = {
|
|
536
|
+
display: "inline-flex",
|
|
537
|
+
alignItems: "center",
|
|
538
|
+
gap: s.gap,
|
|
539
|
+
height: s.height,
|
|
540
|
+
paddingLeft: s.padX,
|
|
541
|
+
paddingRight: s.padX,
|
|
542
|
+
fontSize: s.font,
|
|
543
|
+
fontWeight: 500,
|
|
544
|
+
borderRadius: 9999,
|
|
545
|
+
background: t.bg,
|
|
546
|
+
border: `1px solid ${t.border}`,
|
|
547
|
+
color: t.brand,
|
|
548
|
+
textDecoration: "none",
|
|
549
|
+
userSelect: "none",
|
|
550
|
+
lineHeight: 1
|
|
551
|
+
};
|
|
552
|
+
return /* @__PURE__ */ React.createElement(
|
|
553
|
+
"a",
|
|
554
|
+
{
|
|
555
|
+
href,
|
|
556
|
+
target: "_blank",
|
|
557
|
+
rel: "noopener noreferrer",
|
|
558
|
+
className,
|
|
559
|
+
style,
|
|
560
|
+
"data-elvix-secured-badge": ""
|
|
561
|
+
},
|
|
562
|
+
/* @__PURE__ */ React.createElement(ElvixShield, { size: s.icon, fill: t.shield, accent: accentColor }),
|
|
563
|
+
/* @__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"))
|
|
564
|
+
);
|
|
321
565
|
}
|
|
322
566
|
|
|
323
567
|
// src/react/hooks.ts
|
|
324
|
-
import { useCallback, useEffect as useEffect2, useState as
|
|
568
|
+
import { useCallback, useEffect as useEffect2, useState as useState4 } from "react";
|
|
325
569
|
var POLL_MS = 7e3;
|
|
326
570
|
function useUserList(kind, opts) {
|
|
327
571
|
const { applicationId, baseUrl = "", pollMs = POLL_MS } = opts;
|
|
328
|
-
const [slugs, setSlugs] =
|
|
329
|
-
const [loading, setLoading] =
|
|
330
|
-
const [error, setError] =
|
|
572
|
+
const [slugs, setSlugs] = useState4([]);
|
|
573
|
+
const [loading, setLoading] = useState4(true);
|
|
574
|
+
const [error, setError] = useState4(null);
|
|
331
575
|
const refresh = useCallback(async () => {
|
|
332
576
|
setError(null);
|
|
333
577
|
try {
|
|
@@ -394,7 +638,7 @@ function ElvixLifecycleWatcher({
|
|
|
394
638
|
}
|
|
395
639
|
|
|
396
640
|
// src/react/elvix-username.tsx
|
|
397
|
-
import { useState as
|
|
641
|
+
import { useState as useState5 } from "react";
|
|
398
642
|
|
|
399
643
|
// src/react/lib.ts
|
|
400
644
|
async function appPost(opts, path, body) {
|
|
@@ -456,10 +700,10 @@ function ElvixUsername({
|
|
|
456
700
|
onResult
|
|
457
701
|
}) {
|
|
458
702
|
const ctx = useElvixContext();
|
|
459
|
-
const [value, setValue] =
|
|
460
|
-
const [busy, setBusy] =
|
|
461
|
-
const [error, setError] =
|
|
462
|
-
const [done, setDone] =
|
|
703
|
+
const [value, setValue] = useState5("");
|
|
704
|
+
const [busy, setBusy] = useState5(false);
|
|
705
|
+
const [error, setError] = useState5(null);
|
|
706
|
+
const [done, setDone] = useState5(null);
|
|
463
707
|
async function submit(e) {
|
|
464
708
|
e.preventDefault();
|
|
465
709
|
if (!ctx.app) return;
|
|
@@ -497,14 +741,14 @@ function ElvixUsername({
|
|
|
497
741
|
}
|
|
498
742
|
|
|
499
743
|
// src/react/elvix-avatar.tsx
|
|
500
|
-
import { useState as
|
|
744
|
+
import { useState as useState6 } from "react";
|
|
501
745
|
function ElvixAvatar({
|
|
502
746
|
onResult
|
|
503
747
|
}) {
|
|
504
748
|
const ctx = useElvixContext();
|
|
505
|
-
const [busy, setBusy] =
|
|
506
|
-
const [error, setError] =
|
|
507
|
-
const [preview, setPreview] =
|
|
749
|
+
const [busy, setBusy] = useState6(false);
|
|
750
|
+
const [error, setError] = useState6(null);
|
|
751
|
+
const [preview, setPreview] = useState6(null);
|
|
508
752
|
async function onFile(e) {
|
|
509
753
|
const file = e.target.files?.[0];
|
|
510
754
|
if (!file || !ctx.app) return;
|
|
@@ -538,14 +782,14 @@ function ElvixAvatar({
|
|
|
538
782
|
}
|
|
539
783
|
|
|
540
784
|
// src/react/elvix-banner.tsx
|
|
541
|
-
import { useState as
|
|
785
|
+
import { useState as useState7 } from "react";
|
|
542
786
|
function ElvixBanner({
|
|
543
787
|
onResult
|
|
544
788
|
}) {
|
|
545
789
|
const ctx = useElvixContext();
|
|
546
|
-
const [busy, setBusy] =
|
|
547
|
-
const [error, setError] =
|
|
548
|
-
const [preview, setPreview] =
|
|
790
|
+
const [busy, setBusy] = useState7(false);
|
|
791
|
+
const [error, setError] = useState7(null);
|
|
792
|
+
const [preview, setPreview] = useState7(null);
|
|
549
793
|
async function onFile(e) {
|
|
550
794
|
const file = e.target.files?.[0];
|
|
551
795
|
if (!file || !ctx.app) return;
|
|
@@ -579,18 +823,18 @@ function ElvixBanner({
|
|
|
579
823
|
}
|
|
580
824
|
|
|
581
825
|
// src/react/elvix-identity-form.tsx
|
|
582
|
-
import { useState as
|
|
826
|
+
import { useState as useState8 } from "react";
|
|
583
827
|
function ElvixIdentityForm({
|
|
584
828
|
initialName = "",
|
|
585
829
|
initialBio = "",
|
|
586
830
|
onResult
|
|
587
831
|
}) {
|
|
588
832
|
const ctx = useElvixContext();
|
|
589
|
-
const [name, setName] =
|
|
590
|
-
const [bio, setBio] =
|
|
591
|
-
const [busy, setBusy] =
|
|
592
|
-
const [error, setError] =
|
|
593
|
-
const [saved, setSaved] =
|
|
833
|
+
const [name, setName] = useState8(initialName);
|
|
834
|
+
const [bio, setBio] = useState8(initialBio);
|
|
835
|
+
const [busy, setBusy] = useState8(false);
|
|
836
|
+
const [error, setError] = useState8(null);
|
|
837
|
+
const [saved, setSaved] = useState8(false);
|
|
594
838
|
async function submit(e) {
|
|
595
839
|
e.preventDefault();
|
|
596
840
|
if (!ctx.app) return;
|
|
@@ -610,18 +854,18 @@ function ElvixIdentityForm({
|
|
|
610
854
|
}
|
|
611
855
|
|
|
612
856
|
// src/react/elvix-region.tsx
|
|
613
|
-
import { useState as
|
|
857
|
+
import { useState as useState9 } from "react";
|
|
614
858
|
function ElvixRegion({
|
|
615
859
|
initialCountry = "",
|
|
616
860
|
initialTimezone = "",
|
|
617
861
|
onResult
|
|
618
862
|
}) {
|
|
619
863
|
const ctx = useElvixContext();
|
|
620
|
-
const [country, setCountry] =
|
|
621
|
-
const [timezone, setTimezone] =
|
|
622
|
-
const [busy, setBusy] =
|
|
623
|
-
const [error, setError] =
|
|
624
|
-
const [saved, setSaved] =
|
|
864
|
+
const [country, setCountry] = useState9(initialCountry);
|
|
865
|
+
const [timezone, setTimezone] = useState9(initialTimezone);
|
|
866
|
+
const [busy, setBusy] = useState9(false);
|
|
867
|
+
const [error, setError] = useState9(null);
|
|
868
|
+
const [saved, setSaved] = useState9(false);
|
|
625
869
|
async function submit(e) {
|
|
626
870
|
e.preventDefault();
|
|
627
871
|
if (!ctx.app) return;
|
|
@@ -641,16 +885,16 @@ function ElvixRegion({
|
|
|
641
885
|
}
|
|
642
886
|
|
|
643
887
|
// src/react/elvix-languages.tsx
|
|
644
|
-
import { useState as
|
|
888
|
+
import { useState as useState10 } from "react";
|
|
645
889
|
function ElvixLanguages({
|
|
646
890
|
initial = [],
|
|
647
891
|
onResult
|
|
648
892
|
}) {
|
|
649
893
|
const ctx = useElvixContext();
|
|
650
|
-
const [raw, setRaw] =
|
|
651
|
-
const [busy, setBusy] =
|
|
652
|
-
const [error, setError] =
|
|
653
|
-
const [saved, setSaved] =
|
|
894
|
+
const [raw, setRaw] = useState10(initial.join(", "));
|
|
895
|
+
const [busy, setBusy] = useState10(false);
|
|
896
|
+
const [error, setError] = useState10(null);
|
|
897
|
+
const [saved, setSaved] = useState10(false);
|
|
654
898
|
async function submit(e) {
|
|
655
899
|
e.preventDefault();
|
|
656
900
|
if (!ctx.app) return;
|
|
@@ -671,18 +915,18 @@ function ElvixLanguages({
|
|
|
671
915
|
}
|
|
672
916
|
|
|
673
917
|
// src/react/elvix-sessions.tsx
|
|
674
|
-
import { useEffect as useEffect4, useState as
|
|
918
|
+
import { useEffect as useEffect4, useState as useState11 } from "react";
|
|
675
919
|
function ElvixSessions({
|
|
676
920
|
onResult
|
|
677
921
|
}) {
|
|
678
922
|
const ctx = useElvixContext();
|
|
679
|
-
const [rows, setRows] =
|
|
680
|
-
const [error, setError] =
|
|
681
|
-
const [busy, setBusy] =
|
|
923
|
+
const [rows, setRows] = useState11(null);
|
|
924
|
+
const [error, setError] = useState11(null);
|
|
925
|
+
const [busy, setBusy] = useState11(false);
|
|
682
926
|
useEffect4(() => {
|
|
683
927
|
if (!ctx.app) return;
|
|
684
928
|
fetch(`${ctx.baseUrl}/api/account/apps/${ctx.app.applicationId}/sessions`, {
|
|
685
|
-
|
|
929
|
+
...authInit()
|
|
686
930
|
}).then((r) => r.json()).then((j) => {
|
|
687
931
|
if (j.success && j.data) setRows(j.data.sessions);
|
|
688
932
|
else setError("load_failed");
|
|
@@ -703,14 +947,14 @@ function ElvixSessions({
|
|
|
703
947
|
}
|
|
704
948
|
|
|
705
949
|
// src/react/elvix-export.tsx
|
|
706
|
-
import { useState as
|
|
950
|
+
import { useState as useState12 } from "react";
|
|
707
951
|
function ElvixExport({
|
|
708
952
|
onResult
|
|
709
953
|
}) {
|
|
710
954
|
const ctx = useElvixContext();
|
|
711
|
-
const [busy, setBusy] =
|
|
712
|
-
const [done, setDone] =
|
|
713
|
-
const [error, setError] =
|
|
955
|
+
const [busy, setBusy] = useState12(false);
|
|
956
|
+
const [done, setDone] = useState12(false);
|
|
957
|
+
const [error, setError] = useState12(null);
|
|
714
958
|
async function start() {
|
|
715
959
|
if (!ctx.app) return;
|
|
716
960
|
setBusy(true);
|
|
@@ -729,16 +973,16 @@ function ElvixExport({
|
|
|
729
973
|
}
|
|
730
974
|
|
|
731
975
|
// src/react/elvix-deactivate.tsx
|
|
732
|
-
import { useState as
|
|
976
|
+
import { useState as useState13 } from "react";
|
|
733
977
|
function ElvixDeactivate({
|
|
734
978
|
onResult
|
|
735
979
|
}) {
|
|
736
980
|
const ctx = useElvixContext();
|
|
737
|
-
const [pane, setPane] =
|
|
738
|
-
const [challengeId, setChallengeId] =
|
|
739
|
-
const [code, setCode] =
|
|
740
|
-
const [busy, setBusy] =
|
|
741
|
-
const [error, setError] =
|
|
981
|
+
const [pane, setPane] = useState13("warn");
|
|
982
|
+
const [challengeId, setChallengeId] = useState13(null);
|
|
983
|
+
const [code, setCode] = useState13("");
|
|
984
|
+
const [busy, setBusy] = useState13(false);
|
|
985
|
+
const [error, setError] = useState13(null);
|
|
742
986
|
async function startChallenge() {
|
|
743
987
|
if (!ctx.app) return;
|
|
744
988
|
setBusy(true);
|
|
@@ -795,16 +1039,16 @@ function ElvixDeactivate({
|
|
|
795
1039
|
}
|
|
796
1040
|
|
|
797
1041
|
// src/react/elvix-leave.tsx
|
|
798
|
-
import { useState as
|
|
1042
|
+
import { useState as useState14 } from "react";
|
|
799
1043
|
function ElvixLeave({
|
|
800
1044
|
onResult
|
|
801
1045
|
}) {
|
|
802
1046
|
const ctx = useElvixContext();
|
|
803
|
-
const [pane, setPane] =
|
|
804
|
-
const [challengeId, setChallengeId] =
|
|
805
|
-
const [code, setCode] =
|
|
806
|
-
const [busy, setBusy] =
|
|
807
|
-
const [error, setError] =
|
|
1047
|
+
const [pane, setPane] = useState14("warn");
|
|
1048
|
+
const [challengeId, setChallengeId] = useState14(null);
|
|
1049
|
+
const [code, setCode] = useState14("");
|
|
1050
|
+
const [busy, setBusy] = useState14(false);
|
|
1051
|
+
const [error, setError] = useState14(null);
|
|
808
1052
|
async function startChallenge() {
|
|
809
1053
|
if (!ctx.app) return;
|
|
810
1054
|
setBusy(true);
|
|
@@ -861,16 +1105,16 @@ function ElvixLeave({
|
|
|
861
1105
|
}
|
|
862
1106
|
|
|
863
1107
|
// src/react/elvix-address-book.tsx
|
|
864
|
-
import { useEffect as useEffect5, useState as
|
|
1108
|
+
import { useEffect as useEffect5, useState as useState15 } from "react";
|
|
865
1109
|
function ElvixAddressBook({
|
|
866
1110
|
onResult
|
|
867
1111
|
}) {
|
|
868
1112
|
const ctx = useElvixContext();
|
|
869
|
-
const [rows, setRows] =
|
|
870
|
-
const [error, setError] =
|
|
871
|
-
const [busy, setBusy] =
|
|
872
|
-
const [adding, setAdding] =
|
|
873
|
-
const [form, setForm] =
|
|
1113
|
+
const [rows, setRows] = useState15(null);
|
|
1114
|
+
const [error, setError] = useState15(null);
|
|
1115
|
+
const [busy, setBusy] = useState15(false);
|
|
1116
|
+
const [adding, setAdding] = useState15(false);
|
|
1117
|
+
const [form, setForm] = useState15({
|
|
874
1118
|
label: "Home",
|
|
875
1119
|
line1: "",
|
|
876
1120
|
postalCode: "",
|
|
@@ -880,7 +1124,7 @@ function ElvixAddressBook({
|
|
|
880
1124
|
function reload() {
|
|
881
1125
|
if (!ctx.app) return;
|
|
882
1126
|
fetch(`${ctx.baseUrl}/api/account/apps/${ctx.app.applicationId}/addresses`, {
|
|
883
|
-
|
|
1127
|
+
...authInit()
|
|
884
1128
|
}).then((r) => r.json()).then((j) => {
|
|
885
1129
|
if (j.success && j.data) setRows(j.data.addresses);
|
|
886
1130
|
else setError("load_failed");
|
|
@@ -923,16 +1167,16 @@ function ElvixAddressBook({
|
|
|
923
1167
|
}
|
|
924
1168
|
|
|
925
1169
|
// src/react/elvix-legal-entities.tsx
|
|
926
|
-
import { useEffect as useEffect6, useState as
|
|
1170
|
+
import { useEffect as useEffect6, useState as useState16 } from "react";
|
|
927
1171
|
function ElvixLegalEntities({
|
|
928
1172
|
onResult
|
|
929
1173
|
}) {
|
|
930
1174
|
const ctx = useElvixContext();
|
|
931
|
-
const [rows, setRows] =
|
|
932
|
-
const [error, setError] =
|
|
933
|
-
const [busy, setBusy] =
|
|
934
|
-
const [adding, setAdding] =
|
|
935
|
-
const [form, setForm] =
|
|
1175
|
+
const [rows, setRows] = useState16(null);
|
|
1176
|
+
const [error, setError] = useState16(null);
|
|
1177
|
+
const [busy, setBusy] = useState16(false);
|
|
1178
|
+
const [adding, setAdding] = useState16(false);
|
|
1179
|
+
const [form, setForm] = useState16({
|
|
936
1180
|
legalName: "",
|
|
937
1181
|
taxId: "",
|
|
938
1182
|
country: ""
|
|
@@ -940,7 +1184,7 @@ function ElvixLegalEntities({
|
|
|
940
1184
|
function reload() {
|
|
941
1185
|
if (!ctx.app) return;
|
|
942
1186
|
fetch(`${ctx.baseUrl}/api/account/apps/${ctx.app.applicationId}/legal-entities`, {
|
|
943
|
-
|
|
1187
|
+
...authInit()
|
|
944
1188
|
}).then((r) => r.json()).then((j) => {
|
|
945
1189
|
if (j.success && j.data) setRows(j.data.entities);
|
|
946
1190
|
else setError("load_failed");
|
|
@@ -982,6 +1226,7 @@ function ElvixLegalEntities({
|
|
|
982
1226
|
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")));
|
|
983
1227
|
}
|
|
984
1228
|
export {
|
|
1229
|
+
DEFAULT_COPY,
|
|
985
1230
|
ElvixAddressBook,
|
|
986
1231
|
ElvixAvatar,
|
|
987
1232
|
ElvixBanner,
|
|
@@ -995,8 +1240,10 @@ export {
|
|
|
995
1240
|
ElvixLifecycleWatcher,
|
|
996
1241
|
ElvixProvider,
|
|
997
1242
|
ElvixRegion,
|
|
1243
|
+
ElvixSecuredBadge,
|
|
998
1244
|
ElvixSessions,
|
|
999
1245
|
ElvixSignIn,
|
|
1246
|
+
ElvixSignInButton,
|
|
1000
1247
|
ElvixUsername,
|
|
1001
1248
|
getElvixToken,
|
|
1002
1249
|
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.2",
|
|
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",
|