@axium/client 0.14.3 → 0.14.5

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/requests.js CHANGED
@@ -28,7 +28,7 @@ export async function fetchAPI(method, endpoint, data, ...params) {
28
28
  options.body = JSON.stringify(data);
29
29
  const search = method != 'GET' || typeof data != 'object' || data == null || !Object.keys(data).length
30
30
  ? ''
31
- : '?' + new URLSearchParams(data).toString();
31
+ : '?' + new URLSearchParams(JSON.parse(JSON.stringify(data))).toString();
32
32
  if (token)
33
33
  options.headers.Authorization = 'Bearer ' + token;
34
34
  const parts = [];
@@ -4,8 +4,9 @@
4
4
  let {
5
5
  children,
6
6
  dialog = $bindable(),
7
- submitText = 'Submit',
8
7
  cancel = () => {},
8
+ clearOnCancel = false,
9
+ submitText = 'Submit',
9
10
  submit = (data: object): Promise<any> => Promise.resolve(),
10
11
  pageMode = false,
11
12
  submitDanger = false,
@@ -15,10 +16,11 @@
15
16
  }: {
16
17
  children(): any;
17
18
  dialog?: HTMLDialogElement;
19
+ clearOnCancel?: boolean;
20
+ /** A callback for when the dialog is canceled */
21
+ cancel?(): unknown;
18
22
  /** Change the text displayed for the submit button */
19
23
  submitText?: string;
20
- /** Basically a callback for when the dialog is canceled */
21
- cancel?(): unknown;
22
24
  /** Called on submission, this should do the actual submission */
23
25
  submit?(data: Record<string, FormDataEntryValue>): Promise<any>;
24
26
  /** Whether to display the dialog as a full-page form */
@@ -36,6 +38,7 @@
36
38
 
37
39
  function onclose(e: Event) {
38
40
  e.preventDefault();
41
+ if (clearOnCancel) dialog!.querySelector('form')!.reset();
39
42
  cancel();
40
43
  }
41
44
 
@@ -1,22 +1,25 @@
1
1
  <script lang="ts">
2
2
  import { fetchAPI } from '@axium/client/requests';
3
- import { getUserImage, type AccessTarget, type UserPublic } from '@axium/core';
3
+ import { getUserImage, type UserPublic } from '@axium/core';
4
4
  import { colorHash } from '@axium/core/color';
5
- import Icon from './Icon.svelte';
6
5
  import { errorText } from '@axium/core/io';
6
+ import Icon from './Icon.svelte';
7
7
 
8
8
  const {
9
9
  onSelect,
10
10
  enableTags = false,
11
11
  excludeTargets = [],
12
+ noRoles = false,
13
+ allowExact,
12
14
  }: {
13
- onSelect(target: AccessTarget): unknown;
15
+ onSelect(target: string): unknown;
14
16
  enableTags?: boolean;
15
17
  excludeTargets?: string[];
18
+ noRoles?: boolean;
19
+ allowExact?: boolean;
16
20
  } = $props();
17
21
 
18
- type Result = { type: 'user'; value: UserPublic; target: string } | { type: 'role' | 'tag'; value: string; target: string };
19
-
22
+ type Result = { type: 'user'; value: UserPublic; target: string } | { type: 'role' | 'tag' | 'exact'; value: string; target: string };
20
23
  let results = $state<Result[]>([]);
21
24
  let value = $state<string>();
22
25
  let gotError = $state<boolean>(false);
@@ -30,8 +33,9 @@
30
33
  try {
31
34
  const users = await fetchAPI('POST', 'users/discover', value);
32
35
  results = [
36
+ allowExact && ({ type: 'exact', value, target: value } as const),
33
37
  ...users.map(value => ({ type: 'user', value, target: value.id }) as const),
34
- { type: 'role', value, target: '@' + value } as const,
38
+ !noRoles && ({ type: 'role', value, target: '@' + value } as const),
35
39
  enableTags && ({ type: 'tag', value, target: '#' + value } as const),
36
40
  ].filter<Result>(r => !!r);
37
41
  } catch (e) {
@@ -52,7 +56,7 @@
52
56
  </script>
53
57
 
54
58
  <input bind:value type="text" placeholder="Add users and roles" {onchange} onkeyup={onchange} />
55
- {#if !gotError}
59
+ {#if !gotError && value}
56
60
  <!-- Don't show results when we can't use the discovery API -->
57
61
  <div class="results">
58
62
  {#each results as result}
@@ -72,6 +76,8 @@
72
76
  ><Icon i="hashtag" />{result.value}</span
73
77
  >
74
78
  </span>
79
+ {:else if result.type == 'exact'}
80
+ <span class="non-user">{result.value}</span>
75
81
  {/if}
76
82
  </div>
77
83
  {/if}
@@ -119,6 +125,14 @@
119
125
  .result {
120
126
  padding: 0.5em;
121
127
  border-radius: 0.5em;
128
+
129
+ .non-user {
130
+ border-radius: 1em;
131
+ padding: 0.25em 0.75em;
132
+ display: inline-flex;
133
+ align-items: center;
134
+ gap: 0.25em;
135
+ }
122
136
  }
123
137
 
124
138
  .result:hover {
@@ -126,14 +140,6 @@
126
140
  background-color: var(--bg-strong);
127
141
  }
128
142
 
129
- .tag-or-role {
130
- border-radius: 1em;
131
- padding: 0.25em 0.75em;
132
- display: inline-flex;
133
- align-items: center;
134
- gap: 0.25em;
135
- }
136
-
137
143
  img {
138
144
  width: 2em;
139
145
  height: 2em;
@@ -17,8 +17,8 @@ export interface ContextMenuItem {
17
17
  /**
18
18
  * Attach a context menu to an element with the given actions
19
19
  */
20
- export function contextMenu(...menuItems: (ContextMenuItem | false | null | undefined)[]) {
21
- function _attachContextMenu(element: HTMLElement) {
20
+ export function contextMenu(...menuItems: (ContextMenuItem | false | null | undefined)[]): Attachment<HTMLElement> {
21
+ return function _attachContextMenu(element: HTMLElement) {
22
22
  const menu = document.createElement('div');
23
23
  menu.popover = 'auto';
24
24
  menu.className = 'context-menu';
@@ -88,6 +88,29 @@ export function contextMenu(...menuItems: (ContextMenuItem | false | null | unde
88
88
  for (const icon of mountedIcons) unmount(icon);
89
89
  menu.remove();
90
90
  };
91
- }
92
- return _attachContextMenu satisfies Attachment<HTMLElement>;
91
+ };
92
+ }
93
+
94
+ export function dynamicRows(max: number = 40, min: number = 3): Attachment<HTMLTextAreaElement> {
95
+ return function _attackDynamicRows(element: HTMLTextAreaElement) {
96
+ element.style.resize = 'none';
97
+ // @ts-expect-error field-sizing is not yet in the types
98
+ element.style.fieldSizing = 'content';
99
+ element.style.height = 'max-content';
100
+ element.style.overflowY = 'scroll';
101
+
102
+ function update() {
103
+ if (!element.value) return;
104
+ element.rows = Math.max(Math.min(element.value.split('\n').length, max), min);
105
+ }
106
+
107
+ if (!navigator.userAgent.includes('Firefox')) return;
108
+ update();
109
+ element.addEventListener('input', update);
110
+ element.addEventListener('keyup', update);
111
+ return () => {
112
+ element.removeEventListener('input', update);
113
+ element.removeEventListener('keyup', update);
114
+ };
115
+ };
93
116
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axium/client",
3
- "version": "0.14.3",
3
+ "version": "0.14.5",
4
4
  "author": "James Prevett <jp@jamespre.dev>",
5
5
  "funding": {
6
6
  "type": "individual",