@axium/client 0.9.13 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/styles.css +50 -0
- package/dist/cli/config.d.ts +2 -0
- package/dist/config.d.ts +6 -4
- package/dist/requests.d.ts +3 -2
- package/dist/requests.js +19 -1
- package/dist/user.d.ts +5 -1
- package/dist/user.js +19 -54
- package/lib/FormDialog.svelte +2 -3
- package/lib/Preference.svelte +35 -25
- package/lib/URLText.svelte +21 -0
- package/lib/index.ts +1 -1
- package/package.json +2 -2
- package/lib/Dialog.svelte +0 -53
package/assets/styles.css
CHANGED
|
@@ -101,6 +101,53 @@ pre {
|
|
|
101
101
|
border-radius: 0.5em;
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
+
dialog {
|
|
105
|
+
border-radius: 1em;
|
|
106
|
+
background: var(--bg-menu);
|
|
107
|
+
border: 1px solid #8888;
|
|
108
|
+
padding: 1em;
|
|
109
|
+
max-width: calc(100% - 2em);
|
|
110
|
+
word-wrap: normal;
|
|
111
|
+
|
|
112
|
+
form {
|
|
113
|
+
max-width: 100%;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
dialog::backdrop {
|
|
118
|
+
background: #0003;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
dialog[open] {
|
|
122
|
+
animation: zoom 0.25s cubic-bezier(0.35, 1.55, 0.65, 1);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
@keyframes zoom {
|
|
126
|
+
from {
|
|
127
|
+
transform: scale(0.95);
|
|
128
|
+
}
|
|
129
|
+
to {
|
|
130
|
+
transform: scale(1);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
dialog[open]::backdrop {
|
|
135
|
+
animation: fade 0.25s ease-out;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
@keyframes fade {
|
|
139
|
+
from {
|
|
140
|
+
opacity: 0;
|
|
141
|
+
}
|
|
142
|
+
to {
|
|
143
|
+
opacity: 1;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
dialog form {
|
|
148
|
+
display: contents;
|
|
149
|
+
}
|
|
150
|
+
|
|
104
151
|
.error {
|
|
105
152
|
padding: 1em;
|
|
106
153
|
border-radius: 0.5em;
|
|
@@ -208,6 +255,9 @@ h6 {
|
|
|
208
255
|
}
|
|
209
256
|
|
|
210
257
|
@media (width < 700px) {
|
|
258
|
+
dialog {
|
|
259
|
+
}
|
|
260
|
+
|
|
211
261
|
.mobile-hide {
|
|
212
262
|
display: none !important;
|
|
213
263
|
}
|
package/dist/cli/config.d.ts
CHANGED
|
@@ -14,8 +14,10 @@ export declare function session(): {
|
|
|
14
14
|
debug: boolean;
|
|
15
15
|
};
|
|
16
16
|
roles: string[];
|
|
17
|
+
tags: string[];
|
|
17
18
|
registeredAt: Date;
|
|
18
19
|
isAdmin: boolean;
|
|
20
|
+
isSuspended: boolean;
|
|
19
21
|
emailVerified?: Date | null | undefined;
|
|
20
22
|
image?: string | null | undefined;
|
|
21
23
|
};
|
package/dist/config.d.ts
CHANGED
|
@@ -14,14 +14,16 @@ export declare const ClientConfig: z.ZodObject<{
|
|
|
14
14
|
id: z.ZodUUID;
|
|
15
15
|
name: z.ZodString;
|
|
16
16
|
email: z.ZodEmail;
|
|
17
|
-
emailVerified: z.ZodOptional<z.ZodNullable<z.
|
|
17
|
+
emailVerified: z.ZodOptional<z.ZodNullable<z.ZodCoercedDate<unknown>>>;
|
|
18
18
|
image: z.ZodOptional<z.ZodNullable<z.ZodURL>>;
|
|
19
|
-
preferences: z.ZodObject<{
|
|
20
|
-
debug: z.ZodBoolean
|
|
21
|
-
}, z.core.$strip
|
|
19
|
+
preferences: z.ZodLazy<z.ZodObject<{
|
|
20
|
+
debug: z.ZodDefault<z.ZodBoolean>;
|
|
21
|
+
}, z.core.$strip>>;
|
|
22
22
|
roles: z.ZodArray<z.ZodString>;
|
|
23
|
+
tags: z.ZodArray<z.ZodString>;
|
|
23
24
|
registeredAt: z.ZodCoercedDate<unknown>;
|
|
24
25
|
isAdmin: z.ZodBoolean;
|
|
26
|
+
isSuspended: z.ZodBoolean;
|
|
25
27
|
}, z.core.$strip>;
|
|
26
28
|
}, z.core.$strip>;
|
|
27
29
|
apps: z.ZodArray<z.ZodObject<{
|
package/dist/requests.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { APIParameters, APIValues, Endpoint, RequestBody } from '@axium/core/api';
|
|
2
|
+
import { $API } from '@axium/core/api';
|
|
2
3
|
import type { RequestMethod } from '@axium/core/requests';
|
|
3
4
|
export declare let token: string | null;
|
|
4
5
|
export declare function setToken(value: string | null): void;
|
|
5
6
|
export declare let prefix: string;
|
|
6
7
|
export declare function setPrefix(value: string): void;
|
|
7
|
-
export declare function fetchAPI<const
|
|
8
|
+
export declare function fetchAPI<const E extends Endpoint, const M extends keyof $API[E] & RequestMethod>(method: M, endpoint: E, data?: RequestBody<M, E>, ...params: APIParameters<E>): Promise<M extends keyof APIValues[E] ? (APIValues[E][M] extends readonly [unknown, infer R] ? R : APIValues[E][M]) : unknown>;
|
package/dist/requests.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { $API } from '@axium/core/api';
|
|
2
|
+
import { prettifyError } from 'zod';
|
|
1
3
|
export let token = null;
|
|
2
4
|
export function setToken(value) {
|
|
3
5
|
token = value;
|
|
@@ -14,6 +16,14 @@ export async function fetchAPI(method, endpoint, data, ...params) {
|
|
|
14
16
|
Accept: 'application/json',
|
|
15
17
|
},
|
|
16
18
|
};
|
|
19
|
+
const schema = $API[endpoint]?.[method];
|
|
20
|
+
if (schema && Array.isArray(schema))
|
|
21
|
+
try {
|
|
22
|
+
data = schema[0].parse(data);
|
|
23
|
+
}
|
|
24
|
+
catch (e) {
|
|
25
|
+
throw prettifyError(e);
|
|
26
|
+
}
|
|
17
27
|
if (method !== 'GET' && method !== 'HEAD')
|
|
18
28
|
options.body = JSON.stringify(data);
|
|
19
29
|
const search = method != 'GET' || typeof data != 'object' || data == null || !Object.keys(data).length
|
|
@@ -39,5 +49,13 @@ export async function fetchAPI(method, endpoint, data, ...params) {
|
|
|
39
49
|
const json = await response.json().catch(() => ({ message: 'Unknown server error (invalid JSON response)' }));
|
|
40
50
|
if (!response.ok)
|
|
41
51
|
throw new Error(json.message);
|
|
42
|
-
|
|
52
|
+
if (!schema)
|
|
53
|
+
return json;
|
|
54
|
+
const Output = Array.isArray(schema) ? schema[1] : schema;
|
|
55
|
+
try {
|
|
56
|
+
return Output.parse(json);
|
|
57
|
+
}
|
|
58
|
+
catch (e) {
|
|
59
|
+
throw prettifyError(e);
|
|
60
|
+
}
|
|
43
61
|
}
|
package/dist/user.d.ts
CHANGED
|
@@ -19,7 +19,11 @@ export declare function updateUser(userId: string, data: Record<string, FormData
|
|
|
19
19
|
export declare function fullUserInfo(userId: string): Promise<User & {
|
|
20
20
|
sessions: Session[];
|
|
21
21
|
}>;
|
|
22
|
-
|
|
22
|
+
/**
|
|
23
|
+
* @param userId The UUID of the user to delete
|
|
24
|
+
* @param deletingId The UUID of the user performing the deletion (for authentication). Defaults to userId.
|
|
25
|
+
*/
|
|
26
|
+
export declare function deleteUser(userId: string, deletingId?: string): Promise<User>;
|
|
23
27
|
export declare function emailVerificationEnabled(userId: string): Promise<boolean>;
|
|
24
28
|
export declare function sendVerificationEmail(userId: string): Promise<Verification>;
|
|
25
29
|
export declare function verifyEmail(userId: string, token: string): Promise<void>;
|
package/dist/user.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { UserChangeable } from '@axium/core';
|
|
2
1
|
import { startAuthentication, startRegistration } from '@simplewebauthn/browser';
|
|
3
2
|
import * as z from 'zod';
|
|
4
3
|
import { fetchAPI } from './requests.js';
|
|
@@ -23,38 +22,20 @@ export async function loginByEmail(email) {
|
|
|
23
22
|
return await login(userId);
|
|
24
23
|
}
|
|
25
24
|
export async function getCurrentSession() {
|
|
26
|
-
|
|
27
|
-
result.created = new Date(result.created);
|
|
28
|
-
result.expires = new Date(result.expires);
|
|
29
|
-
return result;
|
|
25
|
+
return await fetchAPI('GET', 'session');
|
|
30
26
|
}
|
|
31
27
|
export async function getSessions(userId) {
|
|
32
28
|
_checkId(userId);
|
|
33
|
-
|
|
34
|
-
for (const session of result) {
|
|
35
|
-
session.created = new Date(session.created);
|
|
36
|
-
session.expires = new Date(session.expires);
|
|
37
|
-
}
|
|
38
|
-
return result;
|
|
29
|
+
return await fetchAPI('GET', 'users/:id/sessions', {}, userId);
|
|
39
30
|
}
|
|
40
31
|
export async function logout(userId, ...sessionId) {
|
|
41
32
|
_checkId(userId);
|
|
42
|
-
|
|
43
|
-
for (const session of result) {
|
|
44
|
-
session.created = new Date(session.created);
|
|
45
|
-
session.expires = new Date(session.expires);
|
|
46
|
-
}
|
|
47
|
-
return result;
|
|
33
|
+
return await fetchAPI('DELETE', 'users/:id/sessions', { id: sessionId }, userId);
|
|
48
34
|
}
|
|
49
35
|
export async function logoutAll(userId) {
|
|
50
36
|
_checkId(userId);
|
|
51
37
|
await elevate(userId);
|
|
52
|
-
|
|
53
|
-
for (const session of result) {
|
|
54
|
-
session.created = new Date(session.created);
|
|
55
|
-
session.expires = new Date(session.expires);
|
|
56
|
-
}
|
|
57
|
-
return result;
|
|
38
|
+
return await fetchAPI('DELETE', 'users/:id/sessions', { confirm_all: true }, userId);
|
|
58
39
|
}
|
|
59
40
|
export async function logoutCurrentSession() {
|
|
60
41
|
return await fetchAPI('DELETE', 'session');
|
|
@@ -80,59 +61,43 @@ function _checkId(userId) {
|
|
|
80
61
|
}
|
|
81
62
|
export async function userInfo(userId) {
|
|
82
63
|
_checkId(userId);
|
|
83
|
-
|
|
84
|
-
user.registeredAt = new Date(user.registeredAt);
|
|
85
|
-
user.emailVerified = user.emailVerified ? new Date(user.emailVerified) : null;
|
|
86
|
-
return user;
|
|
64
|
+
return await fetchAPI('GET', 'users/:id', {}, userId);
|
|
87
65
|
}
|
|
88
66
|
export async function updateUser(userId, data) {
|
|
89
67
|
_checkId(userId);
|
|
90
|
-
|
|
91
|
-
throw e instanceof z.core.$ZodError ? z.prettifyError(e) : e;
|
|
92
|
-
});
|
|
93
|
-
const result = await fetchAPI('PATCH', 'users/:id', body, userId);
|
|
94
|
-
result.registeredAt = new Date(result.registeredAt);
|
|
95
|
-
if (result.emailVerified)
|
|
96
|
-
result.emailVerified = new Date(result.emailVerified);
|
|
97
|
-
return result;
|
|
68
|
+
return await fetchAPI('PATCH', 'users/:id', data, userId);
|
|
98
69
|
}
|
|
99
70
|
export async function fullUserInfo(userId) {
|
|
100
71
|
_checkId(userId);
|
|
101
|
-
|
|
102
|
-
result.registeredAt = new Date(result.registeredAt);
|
|
103
|
-
result.emailVerified = new Date(result.emailVerified);
|
|
104
|
-
return result;
|
|
72
|
+
return await fetchAPI('GET', 'users/:id/full', {}, userId);
|
|
105
73
|
}
|
|
106
|
-
|
|
74
|
+
/**
|
|
75
|
+
* @param userId The UUID of the user to delete
|
|
76
|
+
* @param deletingId The UUID of the user performing the deletion (for authentication). Defaults to userId.
|
|
77
|
+
*/
|
|
78
|
+
export async function deleteUser(userId, deletingId = userId) {
|
|
107
79
|
_checkId(userId);
|
|
108
|
-
const options = await fetchAPI('OPTIONS', 'users/:id/auth', { type: 'action' },
|
|
80
|
+
const options = await fetchAPI('OPTIONS', 'users/:id/auth', { type: 'action' }, deletingId);
|
|
109
81
|
const response = await startAuthentication({ optionsJSON: options });
|
|
110
|
-
await fetchAPI('POST', 'users/:id/auth', response,
|
|
111
|
-
|
|
112
|
-
result.registeredAt = new Date(result.registeredAt);
|
|
113
|
-
result.emailVerified = new Date(result.emailVerified);
|
|
114
|
-
return result;
|
|
82
|
+
await fetchAPI('POST', 'users/:id/auth', response, deletingId);
|
|
83
|
+
return await fetchAPI('DELETE', 'users/:id', response, userId);
|
|
115
84
|
}
|
|
116
85
|
export async function emailVerificationEnabled(userId) {
|
|
117
86
|
_checkId(userId);
|
|
118
|
-
const { enabled } = await fetchAPI('OPTIONS', 'users/:id/
|
|
87
|
+
const { enabled } = await fetchAPI('OPTIONS', 'users/:id/verify/email', {}, userId);
|
|
119
88
|
return enabled;
|
|
120
89
|
}
|
|
121
90
|
export async function sendVerificationEmail(userId) {
|
|
122
91
|
_checkId(userId);
|
|
123
|
-
return await fetchAPI('GET', 'users/:id/
|
|
92
|
+
return await fetchAPI('GET', 'users/:id/verify/email', {}, userId);
|
|
124
93
|
}
|
|
125
94
|
export async function verifyEmail(userId, token) {
|
|
126
95
|
_checkId(userId);
|
|
127
|
-
await fetchAPI('POST', 'users/:id/
|
|
96
|
+
await fetchAPI('POST', 'users/:id/verify/email', { token }, userId);
|
|
128
97
|
}
|
|
129
98
|
export async function getPasskeys(userId) {
|
|
130
99
|
_checkId(userId);
|
|
131
|
-
|
|
132
|
-
for (const passkey of result) {
|
|
133
|
-
passkey.createdAt = new Date(passkey.createdAt);
|
|
134
|
-
}
|
|
135
|
-
return result;
|
|
100
|
+
return await fetchAPI('GET', 'users/:id/passkeys', {}, userId);
|
|
136
101
|
}
|
|
137
102
|
/**
|
|
138
103
|
* Create a new passkey for an existing user.
|
package/lib/FormDialog.svelte
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import Dialog from './Dialog.svelte';
|
|
3
2
|
import type { HTMLDialogAttributes } from 'svelte/elements';
|
|
4
3
|
|
|
5
4
|
let {
|
|
@@ -59,7 +58,7 @@
|
|
|
59
58
|
<button type="submit" class={['submit', submitDanger && 'danger']}>{submitText}</button>
|
|
60
59
|
{/snippet}
|
|
61
60
|
|
|
62
|
-
<
|
|
61
|
+
<dialog bind:this={dialog} {onclose} {...rest}>
|
|
63
62
|
{@render header?.()}
|
|
64
63
|
<form {onsubmit} class="main" method="dialog">
|
|
65
64
|
{#if error}
|
|
@@ -76,7 +75,7 @@
|
|
|
76
75
|
{/if}
|
|
77
76
|
</form>
|
|
78
77
|
{@render footer?.()}
|
|
79
|
-
</
|
|
78
|
+
</dialog>
|
|
80
79
|
|
|
81
80
|
<style>
|
|
82
81
|
.actions {
|
package/lib/Preference.svelte
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { fetchAPI } from '@axium/client/requests';
|
|
3
|
-
import {
|
|
3
|
+
import type { Preferences, ZodPref } from '@axium/core';
|
|
4
4
|
import type { HTMLInputAttributes } from 'svelte/elements';
|
|
5
5
|
import { getByString, pick, setByString } from 'utilium';
|
|
6
6
|
import Icon from './Icon.svelte';
|
|
@@ -11,15 +11,16 @@
|
|
|
11
11
|
preferences: Preferences;
|
|
12
12
|
path: string;
|
|
13
13
|
schema: ZodPref;
|
|
14
|
+
defaultValue?: any;
|
|
14
15
|
optional?: boolean;
|
|
15
16
|
}
|
|
16
17
|
|
|
17
|
-
let { preferences = $bindable(), userId, path, schema, optional = false }: Props = $props();
|
|
18
|
+
let { preferences = $bindable(), userId, path, schema, optional = false, defaultValue }: Props = $props();
|
|
18
19
|
const id = $props.id();
|
|
19
20
|
|
|
20
21
|
let input = $state<HTMLInputElement | HTMLSelectElement>()!;
|
|
21
22
|
let checked = $state(schema.def.type == 'boolean' && getByString<boolean>(preferences, path));
|
|
22
|
-
const initialValue = $derived<any>(getByString(preferences, path)
|
|
23
|
+
const initialValue = $derived<any>(getByString(preferences, path));
|
|
23
24
|
|
|
24
25
|
function dateAttr(date: Date | null, format: 'date' | 'time' | 'datetime' | 'time+sec') {
|
|
25
26
|
if (!date) return null;
|
|
@@ -50,77 +51,86 @@
|
|
|
50
51
|
|
|
51
52
|
function onchange(e: Event) {
|
|
52
53
|
const value = schema.parse(input instanceof HTMLInputElement && input.type === 'checkbox' ? input.checked : input.value);
|
|
53
|
-
|
|
54
|
-
|
|
54
|
+
const oldValue = getByString(preferences, path);
|
|
55
|
+
if (value == oldValue) return;
|
|
56
|
+
|
|
57
|
+
if (defaultValue == value) {
|
|
58
|
+
const parts = path.split('.');
|
|
59
|
+
const prop = parts.pop()!;
|
|
60
|
+
delete getByString<Record<string, any>>(preferences, parts.join('.'))[prop];
|
|
61
|
+
} else setByString(preferences, path, value);
|
|
62
|
+
|
|
55
63
|
fetchAPI('PATCH', 'users/:id', { preferences }, userId);
|
|
56
64
|
}
|
|
57
65
|
</script>
|
|
58
66
|
|
|
59
67
|
{#snippet _in(rest: HTMLInputAttributes)}
|
|
60
|
-
<input bind:this={input} {id} {...rest} value={initialValue} {onchange} required={!optional} />
|
|
68
|
+
<input bind:this={input} {id} {...rest} value={initialValue} {onchange} required={!optional} {defaultValue} />
|
|
61
69
|
{/snippet}
|
|
62
70
|
|
|
63
|
-
{#if
|
|
71
|
+
{#if schema.type == 'string'}
|
|
64
72
|
{@render _in({ type: schema.format == 'email' ? 'email' : 'text', ...pick(schema, 'minLength', 'maxLength') })}
|
|
65
|
-
{:else if
|
|
73
|
+
{:else if schema.type == 'number'}
|
|
66
74
|
{@render _in({ type: 'number', min: schema.minValue, max: schema.maxValue, step: schema.format?.includes('int') ? 1 : 0.1 })}
|
|
67
|
-
{:else if
|
|
75
|
+
{:else if schema.type == 'bigint'}
|
|
68
76
|
{@render _in({ type: 'number', min: Number(schema.minValue), max: Number(schema.maxValue), step: 1 })}
|
|
69
|
-
{:else if
|
|
77
|
+
{:else if schema.type == 'boolean'}
|
|
70
78
|
<input bind:checked bind:this={input} {id} type="checkbox" {onchange} required={!optional} />
|
|
71
79
|
<label for={id} class="checkbox">
|
|
72
80
|
{#if checked}<Icon i="check" --size="1.3em" />{/if}
|
|
73
81
|
</label>
|
|
74
|
-
{:else if
|
|
82
|
+
{:else if schema.type == 'date'}
|
|
75
83
|
{@render _in({
|
|
76
84
|
type: 'date',
|
|
77
85
|
min: dateAttr(schema.minDate, 'date'),
|
|
78
86
|
max: dateAttr(schema.maxDate, 'date'),
|
|
79
87
|
})}
|
|
80
|
-
{:else if
|
|
88
|
+
{:else if schema.type == 'file'}
|
|
81
89
|
<!-- todo -->
|
|
82
|
-
{:else if
|
|
90
|
+
{:else if schema.type == 'literal'}
|
|
83
91
|
<select bind:this={input} {id} {onchange} required={!optional}>
|
|
84
92
|
{#each schema.values as value}
|
|
85
93
|
<option {value} selected={initialValue === value}>{value}</option>
|
|
86
94
|
{/each}
|
|
87
95
|
</select>
|
|
88
|
-
{:else if
|
|
96
|
+
{:else if schema.type == 'template_literal'}
|
|
89
97
|
<!-- todo -->
|
|
90
|
-
{:else if
|
|
98
|
+
{:else if schema.type == 'default'}
|
|
99
|
+
<Preference {userId} bind:preferences {path} schema={schema.def.innerType} defaultValue={schema.def.defaultValue} />
|
|
100
|
+
{:else if schema.type == 'nullable' || schema.type == 'optional'}
|
|
91
101
|
<!-- defaults are handled differently -->
|
|
92
|
-
<Preference {userId} bind:preferences {path} schema={schema.def.innerType} optional={true} />
|
|
93
|
-
{:else if
|
|
102
|
+
<Preference {userId} bind:preferences {path} {defaultValue} schema={schema.def.innerType} optional={true} />
|
|
103
|
+
{:else if schema.type == 'array'}
|
|
94
104
|
<div class="pref-sub">
|
|
95
105
|
{#each initialValue, i}
|
|
96
106
|
<div class="pref-record-entry">
|
|
97
|
-
<Preference {userId} bind:preferences path="{path}.{i}" schema={schema.element} />
|
|
107
|
+
<Preference {userId} bind:preferences {defaultValue} path="{path}.{i}" schema={schema.element} />
|
|
98
108
|
</div>
|
|
99
109
|
{/each}
|
|
100
110
|
</div>
|
|
101
|
-
{:else if
|
|
111
|
+
{:else if schema.type == 'record'}
|
|
102
112
|
<div class="pref-sub">
|
|
103
113
|
{#each Object.keys(initialValue) as key}
|
|
104
114
|
<div class="pref-record-entry">
|
|
105
115
|
<label for={id}>{key}</label>
|
|
106
|
-
<Preference {userId} bind:preferences path="{path}.{key}" schema={schema.valueType} />
|
|
116
|
+
<Preference {userId} bind:preferences {defaultValue} path="{path}.{key}" schema={schema.valueType} />
|
|
107
117
|
</div>
|
|
108
118
|
{/each}
|
|
109
119
|
</div>
|
|
110
|
-
{:else if
|
|
120
|
+
{:else if schema.type == 'object'}
|
|
111
121
|
{#each Object.entries(schema.shape) as [key, value]}
|
|
112
122
|
<div class="pref-sub">
|
|
113
123
|
<label for={id}>{key}</label>
|
|
114
|
-
<Preference {userId} bind:preferences path="{path}.{key}" schema={value} />
|
|
124
|
+
<Preference {userId} bind:preferences {defaultValue} path="{path}.{key}" schema={value} />
|
|
115
125
|
</div>
|
|
116
126
|
{/each}
|
|
117
|
-
{:else if
|
|
127
|
+
{:else if schema.type == 'tuple'}
|
|
118
128
|
<div class="pref-sub" data-rest={schema.def.rest}>
|
|
119
129
|
{#each schema.def.items as item, i}
|
|
120
|
-
<Preference {userId} bind:preferences path="{path}.{i}" schema={item} />
|
|
130
|
+
<Preference {userId} bind:preferences {defaultValue} path="{path}.{i}" schema={item} />
|
|
121
131
|
{/each}
|
|
122
132
|
</div>
|
|
123
|
-
{:else if
|
|
133
|
+
{:else if schema.type == 'enum'}
|
|
124
134
|
<select bind:this={input} {id} {onchange} required={!optional}>
|
|
125
135
|
{#each Object.entries(schema.enum) as [key, value]}
|
|
126
136
|
<option {value} selected={initialValue === value}>{key}</option>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import ClipboardCopy from './ClipboardCopy.svelte';
|
|
3
|
+
|
|
4
|
+
const { url }: { url: string } = $props();
|
|
5
|
+
|
|
6
|
+
const href = $derived(new URL(url, location.origin).href);
|
|
7
|
+
</script>
|
|
8
|
+
|
|
9
|
+
<pre class="URLText"><span>{href}</span><ClipboardCopy value={href} --size="16px" /></pre>
|
|
10
|
+
|
|
11
|
+
<style>
|
|
12
|
+
.URLText {
|
|
13
|
+
display: flex;
|
|
14
|
+
align-items: center;
|
|
15
|
+
justify-content: space-between;
|
|
16
|
+
|
|
17
|
+
span {
|
|
18
|
+
overflow-x: scroll;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
</style>
|
package/lib/index.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
export { default as AccessControlDialog } from './AccessControlDialog.svelte';
|
|
2
2
|
export { default as AppMenu } from './AppMenu.svelte';
|
|
3
3
|
export { default as ClipboardCopy } from './ClipboardCopy.svelte';
|
|
4
|
-
export { default as Dialog } from './Dialog.svelte';
|
|
5
4
|
export { default as FormDialog } from './FormDialog.svelte';
|
|
6
5
|
export { default as Icon } from './Icon.svelte';
|
|
7
6
|
export { default as Login } from './Login.svelte';
|
|
@@ -14,6 +13,7 @@ export { default as Register } from './Register.svelte';
|
|
|
14
13
|
export { default as SessionList } from './SessionList.svelte';
|
|
15
14
|
export { default as Toast } from './Toast.svelte';
|
|
16
15
|
export { default as Upload } from './Upload.svelte';
|
|
16
|
+
export { default as URLText } from './URLText.svelte';
|
|
17
17
|
export { default as UserCard } from './UserCard.svelte';
|
|
18
18
|
export { default as UserMenu } from './UserMenu.svelte';
|
|
19
19
|
export { default as Version } from './Version.svelte';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@axium/client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"author": "James Prevett <jp@jamespre.dev>",
|
|
5
5
|
"funding": {
|
|
6
6
|
"type": "individual",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"build": "tsc"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
|
-
"@axium/core": ">=0.
|
|
43
|
+
"@axium/core": ">=0.17.0",
|
|
44
44
|
"utilium": "^2.3.8",
|
|
45
45
|
"zod": "^4.0.5",
|
|
46
46
|
"svelte": "^5.36.0"
|
package/lib/Dialog.svelte
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import type { HTMLDialogAttributes } from 'svelte/elements';
|
|
3
|
-
|
|
4
|
-
let { children, dialog = $bindable(), ...rest }: { children(): any; dialog?: HTMLDialogElement } & HTMLDialogAttributes = $props();
|
|
5
|
-
</script>
|
|
6
|
-
|
|
7
|
-
<dialog bind:this={dialog} {...rest}>
|
|
8
|
-
{@render children()}
|
|
9
|
-
</dialog>
|
|
10
|
-
|
|
11
|
-
<!-- svelte-ignore css_unused_selector -->
|
|
12
|
-
<style>
|
|
13
|
-
dialog {
|
|
14
|
-
border-radius: 1em;
|
|
15
|
-
background: var(--bg-menu);
|
|
16
|
-
border: 1px solid #8888;
|
|
17
|
-
padding: 1em;
|
|
18
|
-
|
|
19
|
-
form {
|
|
20
|
-
display: contents;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
dialog::backdrop {
|
|
25
|
-
background: #0003;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
dialog[open] {
|
|
29
|
-
animation: zoom 0.25s cubic-bezier(0.35, 1.55, 0.65, 1);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
@keyframes zoom {
|
|
33
|
-
from {
|
|
34
|
-
transform: scale(0.95);
|
|
35
|
-
}
|
|
36
|
-
to {
|
|
37
|
-
transform: scale(1);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
dialog[open]::backdrop {
|
|
42
|
-
animation: fade 0.25s ease-out;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
@keyframes fade {
|
|
46
|
-
from {
|
|
47
|
-
opacity: 0;
|
|
48
|
-
}
|
|
49
|
-
to {
|
|
50
|
-
opacity: 1;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
</style>
|