@axium/storage 0.4.0 → 0.4.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/client.d.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import type { StorageItemMetadata, StorageItemUpdate, UserFilesInfo } from './common.js';
2
- import type { ItemSelection } from './selection.js';
3
2
  export declare function parseItem(result: StorageItemMetadata): StorageItemMetadata;
4
3
  export interface UploadOptions {
5
4
  parentId?: string;
@@ -13,8 +12,3 @@ export declare function downloadItem(fileId: string): Promise<Blob>;
13
12
  export declare function updateItemMetadata(fileId: string, metadata: StorageItemUpdate): Promise<StorageItemMetadata>;
14
13
  export declare function deleteItem(fileId: string): Promise<StorageItemMetadata>;
15
14
  export declare function getUserFiles(userId: string): Promise<UserFilesInfo>;
16
- export interface SidebarContext {
17
- selection: ItemSelection<string, StorageItemMetadata>;
18
- items: StorageItemMetadata[];
19
- getDirectory(id: string, assignTo?: StorageItemMetadata[]): Promise<StorageItemMetadata[]>;
20
- }
package/dist/plugin.d.ts CHANGED
@@ -37,8 +37,9 @@ declare const _default: {
37
37
  exports: {
38
38
  ".": string;
39
39
  "./*": string;
40
- "./lib": string;
41
- "./lib/*": string;
40
+ "./sidebar": string;
41
+ "./components": string;
42
+ "./components/*": string;
42
43
  };
43
44
  files: string[];
44
45
  scripts: {
@@ -1,37 +1,20 @@
1
1
  <script lang="ts">
2
- import { getDirectoryMetadata, type SidebarContext } from '@axium/storage/client';
3
2
  import type { StorageItemMetadata } from '@axium/storage/common';
4
- import { ItemSelection } from '@axium/storage/selection';
5
- import { setContext } from 'svelte';
6
3
  import StorageSidebarItem from './StorageSidebarItem.svelte';
4
+ import { items as sb_items, getDirectory } from '@axium/storage/sidebar';
7
5
 
8
- let { root, sidebar = $bindable() }: { root: string | StorageItemMetadata[]; sidebar?: SidebarContext } = $props();
6
+ let { root }: { root: string | StorageItemMetadata[] } = $props();
9
7
 
10
8
  let items = $state<StorageItemMetadata[]>([]);
11
9
 
12
- const allItems: StorageItemMetadata[] = [];
13
-
14
- sidebar = {
15
- selection: new ItemSelection(allItems),
16
- items: allItems,
17
- async getDirectory(id: string, assignTo?: StorageItemMetadata[]) {
18
- const data = await getDirectoryMetadata(id);
19
- this.items.push(...data);
20
- assignTo = data;
21
- return data;
22
- },
23
- };
24
-
25
- setContext('storage:sidebar', () => sidebar);
26
-
27
10
  if (typeof root != 'string') {
28
- allItems.push(...root);
11
+ sb_items.push(...root);
29
12
  items = root;
30
13
  }
31
14
  </script>
32
15
 
33
16
  <div id="StorageSidebar">
34
- {#await typeof root == 'string' ? sidebar.getDirectory(root, items) : root}
17
+ {#await typeof root == 'string' ? getDirectory(root, items) : root}
35
18
  <i>Loading...</i>
36
19
  {:then}
37
20
  {#each items as _, i (_.id)}
@@ -1,42 +1,53 @@
1
1
  <script lang="ts">
2
2
  import * as icon from '@axium/core/icons';
3
3
  import { ClipboardCopy, FormDialog, Icon } from '@axium/server/components';
4
- import { deleteItem, updateItemMetadata, type SidebarContext } from '@axium/storage/client';
4
+ import { deleteItem, updateItemMetadata } from '@axium/storage/client';
5
5
  import type { StorageItemMetadata } from '@axium/storage/common';
6
- import { getContext } from 'svelte';
6
+ import { debug, getDirectory, selection, toggle, toggleRange } from '@axium/storage/sidebar';
7
7
  import StorageSidebarItem from './StorageSidebarItem.svelte';
8
8
 
9
9
  let {
10
10
  item = $bindable(),
11
11
  items = $bindable(),
12
- debug = false,
13
12
  }: {
14
13
  item: StorageItemMetadata;
15
14
  /** The items list for the parent directory */
16
15
  items: StorageItemMetadata[];
17
- debug?: boolean;
18
16
  } = $props();
19
17
 
20
- const sb = getContext<() => SidebarContext>('storage:sidebar')();
21
-
22
18
  const dialogs = $state<Record<string, HTMLDialogElement>>({});
23
19
  let popover = $state<HTMLDivElement>();
24
20
 
25
21
  function oncontextmenu(e: MouseEvent) {
26
22
  e.preventDefault();
27
23
  e.stopPropagation();
28
- popover?.togglePopover();
24
+ popover!.togglePopover();
25
+ _forcePopover = true;
29
26
  }
30
27
 
31
28
  function onclick(e: MouseEvent) {
32
- if (e.shiftKey) sb.selection.toggleRange(item.id);
33
- else if (e.ctrlKey) sb.selection.toggle(item.id);
29
+ if (e.shiftKey) toggleRange(item.id);
30
+ else if (e.ctrlKey) toggle(item.id);
34
31
  else {
35
- sb.selection.clear();
36
- sb.selection.add(item.id);
32
+ selection.clear();
33
+ toggle(item.id);
37
34
  }
38
35
  }
39
36
 
37
+ let _forcePopover = false;
38
+
39
+ /**
40
+ * Workaround for https://github.com/whatwg/html/issues/10905
41
+ * @todo Remove when the problem is fixed.
42
+ */
43
+ function onpointerup(e: PointerEvent) {
44
+ if (!_forcePopover) return;
45
+ e.stopPropagation();
46
+ e.preventDefault();
47
+ popover!.togglePopover();
48
+ _forcePopover = false;
49
+ }
50
+
40
51
  let children = $state<StorageItemMetadata[]>([]);
41
52
  </script>
42
53
 
@@ -47,6 +58,7 @@
47
58
  e.preventDefault();
48
59
  dialogs[name].showModal();
49
60
  }}
61
+ class="action"
50
62
  >
51
63
  <Icon {i} --size="14px" />
52
64
  {text}
@@ -63,12 +75,12 @@
63
75
 
64
76
  {#if item.type == 'inode/directory'}
65
77
  <details>
66
- <summary class={['StorageSidebarItem', sb.selection.has(item.id) && 'selected']} {onclick} {oncontextmenu}>
78
+ <summary class={['StorageSidebarItem', selection.has(item.id) && 'selected']} {onclick} {oncontextmenu} {onpointerup}>
67
79
  <Icon i={icon.forMime(item.type)} />
68
80
  <span class="name">{item.name}</span>
69
81
  </summary>
70
82
  <div>
71
- {#await sb.getDirectory(item.id, children)}
83
+ {#await getDirectory(item.id, children)}
72
84
  <i>Loading...</i>
73
85
  {:then}
74
86
  {#each children as _, i (_.id)}
@@ -80,7 +92,7 @@
80
92
  </div>
81
93
  </details>
82
94
  {:else}
83
- <div class={['StorageSidebarItem', sb.selection.has(item.id) && 'selected']} {onclick} {oncontextmenu}>
95
+ <div class={['StorageSidebarItem', selection.has(item.id) && 'selected']} {onclick} {oncontextmenu} {onpointerup}>
84
96
  <Icon i={icon.forMime(item.type)} />
85
97
  <span class="name">{item.name}</span>
86
98
  </div>
@@ -167,4 +179,10 @@
167
179
  details > div {
168
180
  padding-left: 0.5em;
169
181
  }
182
+
183
+ div.action:hover {
184
+ cursor: pointer;
185
+ background-color: #223;
186
+ border-radius: 0.25em;
187
+ }
170
188
  </style>
@@ -0,0 +1,42 @@
1
+ import type { StorageItemMetadata } from '@axium/storage/common';
2
+ import { getDirectoryMetadata } from '@axium/storage/client';
3
+ import { SvelteSet } from 'svelte/reactivity';
4
+
5
+ export const selection = $state(new SvelteSet());
6
+
7
+ export const items = $state<StorageItemMetadata[]>([]);
8
+
9
+ let lastSelected = $state<string>();
10
+
11
+ export function getLastSelected() {
12
+ return lastSelected;
13
+ }
14
+
15
+ /** @todo move this into user preferences somehow */
16
+ export let debug = false;
17
+
18
+ export async function getDirectory(id: string, assignTo?: StorageItemMetadata[]) {
19
+ const data = await getDirectoryMetadata(id);
20
+ items.push(...data);
21
+ assignTo = data;
22
+ return data;
23
+ }
24
+
25
+ export function toggle(id: string): boolean {
26
+ const has = selection.has(id);
27
+ if (has) selection.delete(id);
28
+ else {
29
+ selection.add(id);
30
+ lastSelected = id;
31
+ }
32
+ return has;
33
+ }
34
+
35
+ export function toggleRange(id: string) {
36
+ const from = items.findIndex(item => item.id === lastSelected);
37
+ const until = items.findIndex(item => item.id === id);
38
+ if (from === -1 || until === -1) return;
39
+
40
+ const range = items.slice(Math.min(from, until), Math.max(from, until) + 1);
41
+ for (const item of range) toggle(item.id);
42
+ }
package/lib/tsconfig.json CHANGED
@@ -2,9 +2,12 @@
2
2
  "extends": "../tsconfig.json",
3
3
  "compilerOptions": {
4
4
  "rootDir": "..",
5
- "noEmit": true
5
+ "noEmit": true,
6
+ "module": "preserve",
7
+ "moduleResolution": "Bundler",
8
+ "allowImportingTsExtensions": true
6
9
  },
7
- "include": ["**/*.svelte"],
10
+ "include": ["**/*.svelte", "**/*.ts"],
8
11
  "exclude": [],
9
12
  "references": [{ "path": ".." }]
10
13
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axium/storage",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "author": "James Prevett <axium@jamespre.dev> (https://jamespre.dev)",
5
5
  "description": "User file storage for Axium",
6
6
  "funding": {
@@ -22,8 +22,9 @@
22
22
  "exports": {
23
23
  ".": "./dist/index.js",
24
24
  "./*": "./dist/*.js",
25
- "./lib": "./lib/index.js",
26
- "./lib/*": "./lib/*.js"
25
+ "./sidebar": "./lib/sidebar.svelte.js",
26
+ "./components": "./lib/index.js",
27
+ "./components/*": "./lib/*.svelte"
27
28
  },
28
29
  "files": [
29
30
  "dist",
@@ -1,17 +0,0 @@
1
- import { SvelteSet } from 'svelte/reactivity';
2
- export declare class ItemSelection<T, V extends {
3
- id: T;
4
- }> extends SvelteSet<T> {
5
- /**
6
- * The list of items that can be selected.
7
- */
8
- items: V[];
9
- constructor(
10
- /**
11
- * The list of items that can be selected.
12
- */
13
- items: V[]);
14
- last?: T;
15
- toggle(id: T): boolean;
16
- toggleRange(id: T): void;
17
- }
package/dist/selection.js DELETED
@@ -1,32 +0,0 @@
1
- import { SvelteSet } from 'svelte/reactivity';
2
- export class ItemSelection extends SvelteSet {
3
- items;
4
- constructor(
5
- /**
6
- * The list of items that can be selected.
7
- */
8
- items) {
9
- super([]);
10
- this.items = items;
11
- }
12
- last;
13
- toggle(id) {
14
- const has = this.has(id);
15
- if (has)
16
- this.delete(id);
17
- else {
18
- this.add(id);
19
- this.last = id;
20
- }
21
- return has;
22
- }
23
- toggleRange(id) {
24
- const from = this.items.findIndex(item => item.id === this.last);
25
- const until = this.items.findIndex(item => item.id === id);
26
- if (from === -1 || until === -1)
27
- return;
28
- const range = this.items.slice(Math.min(from, until), Math.max(from, until) + 1);
29
- for (const item of range)
30
- this.toggle(item.id);
31
- }
32
- }