@securancy/file-explorer 1.0.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.
Files changed (36) hide show
  1. package/README.md +7 -0
  2. package/default-styling.pcss +39 -0
  3. package/dist/components/FileExplorer.svelte +443 -0
  4. package/dist/components/FileExplorer.svelte.d.ts +63 -0
  5. package/dist/components/FileExplorerBreadcrumb.svelte +263 -0
  6. package/dist/components/FileExplorerBreadcrumb.svelte.d.ts +18 -0
  7. package/dist/components/FileExplorerCreateDirectory.svelte +77 -0
  8. package/dist/components/FileExplorerCreateDirectory.svelte.d.ts +24 -0
  9. package/dist/components/FileExplorerDirectoryColumn.svelte +456 -0
  10. package/dist/components/FileExplorerDirectoryColumn.svelte.d.ts +39 -0
  11. package/dist/components/FileExplorerDirectoryIndex.svelte +367 -0
  12. package/dist/components/FileExplorerDirectoryIndex.svelte.d.ts +33 -0
  13. package/dist/components/FileExplorerDisplayModeSwitcher.svelte +300 -0
  14. package/dist/components/FileExplorerDisplayModeSwitcher.svelte.d.ts +19 -0
  15. package/dist/components/FileExplorerFileDetailColumn.svelte +340 -0
  16. package/dist/components/FileExplorerFileDetailColumn.svelte.d.ts +24 -0
  17. package/dist/components/FileIcon.svelte +310 -0
  18. package/dist/components/FileIcon.svelte.d.ts +19 -0
  19. package/dist/file-explorer.pcss +304 -0
  20. package/dist/index.d.ts +3 -0
  21. package/dist/index.js +2 -0
  22. package/dist/models/file-explorer-display-mode.d.ts +4 -0
  23. package/dist/models/file-explorer-display-mode.js +5 -0
  24. package/dist/models/file-item.d.ts +23 -0
  25. package/dist/models/file-item.js +1 -0
  26. package/dist/models/index.d.ts +3 -0
  27. package/dist/models/index.js +3 -0
  28. package/dist/models/translations.d.ts +19 -0
  29. package/dist/models/translations.js +17 -0
  30. package/dist/utilities/file-utilities.d.ts +8 -0
  31. package/dist/utilities/file-utilities.js +49 -0
  32. package/dist/utilities/index.d.ts +1 -0
  33. package/dist/utilities/index.js +1 -0
  34. package/dist/validation/file-upload-schema.d.ts +2 -0
  35. package/dist/validation/file-upload-schema.js +2 -0
  36. package/package.json +72 -0
@@ -0,0 +1,304 @@
1
+ /* eslint-disable */
2
+ @import url("@securancy/svelte-components/styles/custom-media.pcss");
3
+
4
+ .file-explorer {
5
+ position: relative;
6
+ display: flex;
7
+ flex-direction: column;
8
+ height: 100%;
9
+ width: 100%;
10
+ background: var(--file-explorer-background);
11
+ overflow: hidden;
12
+
13
+ &__controls {
14
+ flex-shrink: 0;
15
+ display: flex;
16
+ border-bottom: var(--file-explorer-border-style);
17
+ padding: var(--file-explorer-default-spacing);
18
+ background: var(--file-explorer-background-shade);
19
+ }
20
+
21
+ &__content {
22
+ flex-grow: 1;
23
+ display: flex;
24
+ overflow-x: auto;
25
+ scroll-behavior: smooth;
26
+ }
27
+
28
+ /* Keep the index within screen */
29
+ @media (--screen-sm) {
30
+ &--display-mode-index {
31
+ /* @formatter:off */
32
+ ^&__content {
33
+ /* @formatter:on */
34
+ overflow-x: inherit;
35
+ }
36
+ }
37
+ }
38
+
39
+ &__column,
40
+ &__index {
41
+ flex-shrink: 0;
42
+ border-right: var(--file-explorer-border-style);
43
+ background: var(--file-explorer-background);
44
+ }
45
+
46
+ &__column {
47
+ display: flex;
48
+ flex-direction: column;
49
+ width: var(--file-explorer-column-default-width);
50
+ min-width: var(--file-explorer-column-min-width);
51
+ overflow: hidden;
52
+
53
+ &--file-detail {
54
+ flex-grow: 1;
55
+ width: var(--file-explorer-preview-default-width);
56
+ display: flex;
57
+ flex-direction: column;
58
+ }
59
+
60
+ &--sticky {
61
+ position: sticky;
62
+ left: 0;
63
+ z-index: 1;
64
+ }
65
+
66
+ &::after {
67
+ content: '';
68
+ position: absolute;
69
+ inset: 0;
70
+ pointer-events: none;
71
+ transition: background-color 0.2s ease-in-out;
72
+ }
73
+
74
+ &--dragover {
75
+ cursor: copy;
76
+
77
+ &::after {
78
+ pointer-events: all;
79
+ background-color: var(--file-explorer-background-dragover);
80
+ }
81
+ }
82
+ }
83
+
84
+ &__index {
85
+ display: flex;
86
+ flex-direction: column;
87
+ width: var(--file-explorer-index-default-width);
88
+ overflow: hidden;
89
+
90
+ @media (--screen-sm) {
91
+ flex-shrink: 1;
92
+ }
93
+
94
+ &--hidden {
95
+ display: none;
96
+ }
97
+ }
98
+
99
+ &__column-scroll-container,
100
+ &__index-scroll-container {
101
+ flex-grow: 1;
102
+ overflow-y: auto;
103
+ padding: calc(var(--file-explorer-default-spacing) * 1.5) var(--file-explorer-default-spacing) 0 var(--file-explorer-default-spacing);
104
+ }
105
+
106
+ &__grid {
107
+ display: flex;
108
+ align-items: flex-start;
109
+ flex-wrap: wrap;
110
+ gap: var(--file-explorer-default-spacing);
111
+ }
112
+
113
+ &__breadcrumb {
114
+ /* @todo */
115
+ }
116
+
117
+ &__display-mode-switcher {
118
+ display: flex;
119
+ gap: calc(0.5 * var(--file-explorer-default-spacing));
120
+ margin-left: auto;
121
+ }
122
+
123
+ &__search {
124
+ margin-bottom: var(--file-explorer-default-spacing);
125
+ padding: calc(var(--file-explorer-default-spacing) * 1.5) var(--file-explorer-default-spacing) 0 var(--file-explorer-default-spacing);
126
+ }
127
+
128
+ &__list {
129
+ margin-bottom: var(--file-explorer-default-spacing);
130
+ }
131
+
132
+ &__list-header {
133
+ display: flex;
134
+ justify-content: space-between;
135
+ gap: calc(0.5 * var(--file-explorer-default-spacing));
136
+ width: 100%;
137
+ margin-bottom: calc(0.5 * var(--file-explorer-default-spacing));
138
+ color: var(--file-explorer-list-header-color);
139
+
140
+ span {
141
+ overflow: hidden;
142
+ white-space: nowrap;
143
+ text-overflow: ellipsis;
144
+ }
145
+
146
+ button {
147
+ flex-shrink: 0;
148
+ color: var(--file-explorer-action-primary-color);
149
+ cursor: pointer;
150
+ }
151
+ }
152
+
153
+ &__grid-header {
154
+ width: 100%;
155
+
156
+ button {
157
+ color: var(--file-explorer-action-primary-color);
158
+ cursor: pointer;
159
+ }
160
+ }
161
+
162
+ &__list-item,
163
+ &__grid-item {
164
+ border-radius: var(--file-explorer-list-item-border-radius);
165
+ background: none;
166
+ color: var(--file-explorer-list-item-color);
167
+
168
+ > :global(i) {
169
+ flex-shrink: 0;
170
+ }
171
+
172
+ &:not(&--active):hover {
173
+ background: var(--file-explorer-list-item-background-hover);
174
+ }
175
+
176
+ &--active {
177
+ color: var(--file-explorer-list-item-color-active);
178
+ background: var(--file-explorer-list-item-background-active);
179
+ }
180
+ }
181
+
182
+ &__grid-item {
183
+ display: flex;
184
+ flex-direction: column;
185
+ align-items: center;
186
+
187
+ /* 3 items in a grid, spaced out with default spacing means the width of 2 gaps must be subtracted from width */
188
+ width: calc(33.33% - (0.6666 * var(--file-explorer-default-spacing)));
189
+ padding: 5px 2px;
190
+
191
+ @media (--screen-sm) {
192
+ width: 100px;
193
+ }
194
+
195
+ &--active {
196
+ /* @todo */
197
+ }
198
+
199
+ > span {
200
+ word-break: break-word;
201
+ margin-top: calc(0.25 * var(--file-explorer-default-spacing));
202
+ }
203
+ }
204
+
205
+ &__list-item {
206
+ display: flex;
207
+ align-items: baseline;
208
+ width: 100%;
209
+ text-align: left;
210
+ padding: var(--file-explorer-list-item-spacing);
211
+ white-space: nowrap;
212
+ overflow: hidden;
213
+ text-overflow: ellipsis;
214
+
215
+ > span {
216
+ flex-grow: 1;
217
+ min-width: 0;
218
+ overflow: hidden;
219
+ text-overflow: ellipsis;
220
+ }
221
+
222
+ > :global(i:first-child) {
223
+ margin-right: var(--file-explorer-list-item-spacing);
224
+ }
225
+
226
+ > :global(i:last-child) {
227
+ margin-left: var(--file-explorer-list-item-spacing);
228
+ }
229
+ }
230
+
231
+ &__message {
232
+ display: block;
233
+ padding: var(--file-explorer-list-item-spacing) 0;
234
+ color: var(--file-explorer-message-color);
235
+ }
236
+
237
+ &__image-preview {
238
+ max-width: 100%;
239
+ margin: 0 auto;
240
+ }
241
+
242
+ &-preview {
243
+ &__title {
244
+ font-size: var(--font-size--h1);
245
+ font-weight: var(--font-weight--semi-bold);
246
+ overflow: hidden;
247
+ text-overflow: ellipsis;
248
+ white-space: nowrap;
249
+ }
250
+
251
+ &__download {
252
+ color: var(--file-explorer-action-primary-color);
253
+ font-weight: var(--font-weight--semi-bold);
254
+
255
+ &:hover {
256
+ text-decoration-line: underline;
257
+ }
258
+ }
259
+
260
+ &__info {
261
+ margin-top: 14px;
262
+
263
+ & span {
264
+ color: var(--file-explorer-message-color);
265
+ }
266
+
267
+ button {
268
+ cursor: pointer;
269
+ }
270
+ }
271
+ }
272
+
273
+ &__scroll-back-button {
274
+ position: fixed;
275
+ bottom: var(--file-explorer-default-spacing);
276
+ right: var(--file-explorer-default-spacing);
277
+ z-index: 2;
278
+ background: var(--file-explorer-action-primary-color);
279
+ color: var(--file-explorer-action-primary-color-contrast);
280
+ padding: var(--file-explorer-list-item-spacing);
281
+ border-radius: var(--file-explorer-list-item-border-radius);
282
+ }
283
+
284
+ &__file-icon {
285
+ &--large {
286
+ width: 100%;
287
+ text-align: center;
288
+ padding: calc(2 * var(--file-explorer-default-spacing));
289
+
290
+ :global(i) {
291
+ font-size: var(--file-explorer-preview-icon-size);
292
+ }
293
+ }
294
+ }
295
+
296
+ &__text--semi-bold {
297
+ font-weight: var(--file-explorer-text-semi-bold);
298
+ }
299
+
300
+ &__loader {
301
+ display: flex;
302
+ justify-content: center;
303
+ }
304
+ }
@@ -0,0 +1,3 @@
1
+ export { default as FileExplorer } from './components/FileExplorer.svelte';
2
+ export type * from './components/FileExplorer.svelte';
3
+ export * from './models/index.js';
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export { default as FileExplorer } from './components/FileExplorer.svelte';
2
+ export * from './models/index.js';
@@ -0,0 +1,4 @@
1
+ export declare enum FileExplorerDisplayMode {
2
+ Columns = "columns",
3
+ Index = "index"
4
+ }
@@ -0,0 +1,5 @@
1
+ export var FileExplorerDisplayMode;
2
+ (function (FileExplorerDisplayMode) {
3
+ FileExplorerDisplayMode["Columns"] = "columns";
4
+ FileExplorerDisplayMode["Index"] = "index";
5
+ })(FileExplorerDisplayMode || (FileExplorerDisplayMode = {}));
@@ -0,0 +1,23 @@
1
+ export type FileSystemItem = FileItem | DirectoryItem;
2
+ type FileSystemItemProperties = {
3
+ name: string;
4
+ path: string;
5
+ };
6
+ export type FileItem = {
7
+ type: 'file';
8
+ } & FileSystemItemProperties;
9
+ export type DirectoryItem = {
10
+ type: 'directory';
11
+ children: FileSystemItem[];
12
+ } & FileSystemItemProperties;
13
+ export interface FilePreview {
14
+ url: string;
15
+ fileSize: number;
16
+ createdAt?: Date;
17
+ modifiedAt?: Date;
18
+ }
19
+ export type CreateDirectoryRequest = Pick<FileSystemItem, 'path' | 'name'>;
20
+ export type UploadFilesRequest = Pick<FileItem, 'path'> & {
21
+ files: FileList;
22
+ };
23
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ export * from './file-explorer-display-mode.js';
2
+ export * from './file-item.js';
3
+ export * from './translations.js';
@@ -0,0 +1,3 @@
1
+ export * from './file-explorer-display-mode.js';
2
+ export * from './file-item.js';
3
+ export * from './translations.js';
@@ -0,0 +1,19 @@
1
+ import type { FileSystemItem } from './file-item.js';
2
+ export declare const defaultTranslations: {
3
+ readonly createNewDirectoryModalTitle: "Create new directory";
4
+ readonly cancel: "Cancel";
5
+ readonly create: "Create";
6
+ readonly creating: "Creating...";
7
+ readonly itemTitle: (item: FileSystemItem) => string;
8
+ readonly emptyDirectory: "Empty directory";
9
+ readonly previewFile: "Preview file";
10
+ readonly createdAt: "Created at";
11
+ readonly modifiedAt: "Last modified at";
12
+ readonly deleteFile: "Delete file";
13
+ readonly deleteFileConfirmation: "Are you sure you want to delete:";
14
+ readonly deleting: "Deleting...";
15
+ readonly delete: "Delete";
16
+ readonly createDirectory: "Create directory";
17
+ readonly uploadFile: "Upload file";
18
+ };
19
+ export type DocumentTranslations = typeof defaultTranslations;
@@ -0,0 +1,17 @@
1
+ export const defaultTranslations = {
2
+ createNewDirectoryModalTitle: 'Create new directory',
3
+ cancel: 'Cancel',
4
+ create: 'Create',
5
+ creating: 'Creating...',
6
+ itemTitle: (item) => `Open ${item.type === 'directory' ? 'directory' : 'file'} ${item.name}`,
7
+ emptyDirectory: 'Empty directory',
8
+ previewFile: 'Preview file',
9
+ createdAt: 'Created at',
10
+ modifiedAt: 'Last modified at',
11
+ deleteFile: 'Delete file',
12
+ deleteFileConfirmation: 'Are you sure you want to delete:',
13
+ deleting: 'Deleting...',
14
+ delete: 'Delete',
15
+ createDirectory: 'Create directory',
16
+ uploadFile: 'Upload file',
17
+ };
@@ -0,0 +1,8 @@
1
+ import type { FileSystemItem } from '../models/file-item.js';
2
+ export declare function openItem(item: FileSystemItem, routePrefix: string): Promise<void>;
3
+ export declare function getExtension(path: string): string;
4
+ export declare const imageExtensions: string[];
5
+ export declare const videoExtensions: string[];
6
+ export declare function isImage(path: string): boolean;
7
+ export declare function formatBytes(bytes: number, decimals?: number): string;
8
+ export declare function formatBytesParts(bytes: number, decimals?: number): [number, string];
@@ -0,0 +1,49 @@
1
+ import { goto } from '$app/navigation';
2
+ export async function openItem(item, routePrefix) {
3
+ const itemPath = [
4
+ ...routePrefix.split('/'),
5
+ ...item.path.split('/'),
6
+ item.name,
7
+ ]
8
+ .filter((node) => node !== '')
9
+ .map(encodeURIComponent)
10
+ .join('/');
11
+ await goto(itemPath.startsWith('/') ? itemPath : `/${itemPath}`);
12
+ }
13
+ export function getExtension(path) {
14
+ // extract file name from full path (supports `\\` and `/` separators)
15
+ const basename = path.split(/[/\\]/).pop();
16
+ if (!basename)
17
+ return '';
18
+ const basenameStripped = basename.split('?')[0];
19
+ // get last position of `.`
20
+ const position = basenameStripped.lastIndexOf('.');
21
+ // if file name is empty or `.` not found (-1) or comes first (0)
22
+ if (basenameStripped === '' || position < 1)
23
+ return '';
24
+ // extract extension ignoring `.`
25
+ return basenameStripped.slice(position + 1).toLowerCase();
26
+ }
27
+ export const imageExtensions = [
28
+ 'jpg', 'png', 'gif', 'bmp', 'svg', 'webp', 'jpeg',
29
+ ];
30
+ export const videoExtensions = [
31
+ 'mp4', 'webm', 'mov', 'mpg', 'mpeg',
32
+ ];
33
+ export function isImage(path) {
34
+ const extension = getExtension(path);
35
+ return imageExtensions.includes(extension);
36
+ }
37
+ export function formatBytes(bytes, decimals = 2) {
38
+ const values = formatBytesParts(bytes, decimals);
39
+ return `${values[0]} ${values[1]}`;
40
+ }
41
+ export function formatBytesParts(bytes, decimals = 2) {
42
+ if (bytes === 0)
43
+ return [0, 'Bytes'];
44
+ const k = 1000;
45
+ const dm = decimals < 0 ? 0 : decimals;
46
+ const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
47
+ const index = Math.floor(Math.log(bytes) / Math.log(k));
48
+ return [Number.parseFloat((bytes / Math.pow(k, index)).toFixed(dm)), sizes[index]];
49
+ }
@@ -0,0 +1 @@
1
+ export * from './file-utilities.js';
@@ -0,0 +1 @@
1
+ export * from './file-utilities.js';
@@ -0,0 +1,2 @@
1
+ import { z } from 'zod';
2
+ export declare const fileUploadSchema: z.ZodObject<{}, "strip", z.ZodTypeAny, {}, {}>;
@@ -0,0 +1,2 @@
1
+ import { z } from 'zod';
2
+ export const fileUploadSchema = z.object({});
package/package.json ADDED
@@ -0,0 +1,72 @@
1
+ {
2
+ "name": "@securancy/file-explorer",
3
+ "version": "1.0.0",
4
+ "type": "module",
5
+ "private": false,
6
+ "svelte": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "svelte": "./dist/index.js"
12
+ },
13
+ "./default-styling.pcss": "./default-styling.pcss"
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "./default-styling.pcss",
18
+ "!dist/**/*.test.*",
19
+ "!dist/**/*.spec.*"
20
+ ],
21
+ "devDependencies": {
22
+ "@faker-js/faker": "^8.4.1",
23
+ "@sveltejs/adapter-auto": "^3.2.2",
24
+ "@sveltejs/kit": "^2.5.18",
25
+ "@sveltejs/package": "^2.3.2",
26
+ "@sveltejs/vite-plugin-svelte": "^3.1.1",
27
+ "@types/luxon": "^3.4.2",
28
+ "autoprefixer": "^10.4.19",
29
+ "esm-env": "^1.0.0",
30
+ "fast-cartesian": "^9.0.0",
31
+ "luxon": "^3.4.4",
32
+ "postcss": "^8.4.39",
33
+ "postcss-custom-media": "^10.0.8",
34
+ "postcss-each": "^1.1.0",
35
+ "postcss-easy-import": "^4.0.0",
36
+ "postcss-extend": "^1.0.5",
37
+ "postcss-for": "^2.1.1",
38
+ "postcss-mixins": "^10.0.1",
39
+ "postcss-nested": "^6.2.0",
40
+ "postcss-nested-ancestors": "^3.0.0",
41
+ "publint": "^0.2.9",
42
+ "svelte": "^4.2.18",
43
+ "svelte-check": "^3.8.4",
44
+ "the-new-css-reset": "^1.11.2",
45
+ "tslib": "^2.6.3",
46
+ "typescript": "^5.5.3",
47
+ "vite": "^5.3.4",
48
+ "zod": "^3.23.8",
49
+ "@securancy/eslint-config": "0.2.1",
50
+ "@securancy/stylelint-config": "0.1.2",
51
+ "@securancy/svelte-components": "4.2.2",
52
+ "@securancy/svelte-utilities": "2.0.1",
53
+ "@securancy/validation": "1.0.1"
54
+ },
55
+ "peerDependencies": {
56
+ "svelte": "^4.2.18"
57
+ },
58
+ "scripts": {
59
+ "dev": "vite dev",
60
+ "sync": "svelte-kit sync",
61
+ "build": "npm run package",
62
+ "build:docs": "svelte-kit sync && vite build && npm run package",
63
+ "preview": "vite preview",
64
+ "package": "svelte-package && publint",
65
+ "package-watch": "svelte-package -w",
66
+ "lint": "eslint \"./**/*.*{ts,js,svelte,json}\" --config ../../.eslintrc.json --ignore-path ../../.eslintignore",
67
+ "lint-fix": "npm run lint -- --fix",
68
+ "stylelint": "stylelint \"src/**/*.{pcss,svelte,css}\" --config ../../.stylelintrc.json",
69
+ "stylelint-fix": "npm run stylelint -- --fix",
70
+ "svelte-check": "svelte-check --fail-on-warnings --compiler-warnings \"css-unused-selector:ignore\""
71
+ }
72
+ }