@classic-homes/theme-svelte 1.40.0 → 1.41.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.
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import type { FileMetadata, MediaItem } from '../types/components.js';
|
|
17
17
|
import { cn } from '../utils.js';
|
|
18
|
+
import { acceptLabel, matchesAccept } from '../utils/file-accept.js';
|
|
18
19
|
import { formatDuration } from '../utils/media.js';
|
|
19
20
|
import Button from './Button.svelte';
|
|
20
21
|
import Lightbox from './media/Lightbox.svelte';
|
|
@@ -29,7 +30,12 @@
|
|
|
29
30
|
maxFiles?: number;
|
|
30
31
|
/** Maximum file size in bytes (default: 10MB) */
|
|
31
32
|
maxSizeBytes?: number;
|
|
32
|
-
/**
|
|
33
|
+
/**
|
|
34
|
+
* Accepted file types, as `<input accept>` entries: MIME types
|
|
35
|
+
* (`application/pdf`), wildcards (`image/*`) or extensions (`.dxf`,
|
|
36
|
+
* matched case-insensitively against the file name). A file passes when
|
|
37
|
+
* any entry matches.
|
|
38
|
+
*/
|
|
33
39
|
acceptedTypes?: string[];
|
|
34
40
|
/** Whether upload is disabled */
|
|
35
41
|
disabled?: boolean;
|
|
@@ -107,29 +113,9 @@
|
|
|
107
113
|
/**
|
|
108
114
|
* Readable label for the accepted types shown in the drop zone.
|
|
109
115
|
* Uses the explicit `typesLabel` when provided, otherwise groups the
|
|
110
|
-
* accepted
|
|
116
|
+
* accepted types into friendly names (Images, PDF, Videos, DXF, ...).
|
|
111
117
|
*/
|
|
112
|
-
const acceptedTypesLabel = $derived
|
|
113
|
-
if (typesLabel) return typesLabel;
|
|
114
|
-
|
|
115
|
-
const groups: string[] = [];
|
|
116
|
-
const add = (name: string) => {
|
|
117
|
-
if (!groups.includes(name)) groups.push(name);
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
for (const type of acceptedTypes) {
|
|
121
|
-
if (type.startsWith('image/')) add('Images');
|
|
122
|
-
else if (type === 'application/pdf') add('PDF');
|
|
123
|
-
else if (type.startsWith('video/')) add('Videos');
|
|
124
|
-
else if (type.startsWith('audio/')) add('Audio');
|
|
125
|
-
else {
|
|
126
|
-
const subtype = type.split('/')[1];
|
|
127
|
-
add(subtype ? subtype.toUpperCase() : type);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
return groups.join(', ');
|
|
132
|
-
});
|
|
118
|
+
const acceptedTypesLabel = $derived(typesLabel || acceptLabel(acceptedTypes));
|
|
133
119
|
|
|
134
120
|
// Helper to generate unique ID
|
|
135
121
|
function generateId(): string {
|
|
@@ -143,17 +129,11 @@
|
|
|
143
129
|
return `File "${file.name}" exceeds maximum size of ${maxMB}MB`;
|
|
144
130
|
}
|
|
145
131
|
|
|
146
|
-
//
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
}
|
|
152
|
-
return file.type === type;
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
if (!isAccepted) {
|
|
156
|
-
return `File type "${file.type}" is not allowed`;
|
|
132
|
+
// Same rules as the native `accept` attribute: extensions, wildcards, MIME types
|
|
133
|
+
if (!matchesAccept(file, acceptedTypes)) {
|
|
134
|
+
return file.type
|
|
135
|
+
? `File type "${file.type}" is not allowed`
|
|
136
|
+
: `File "${file.name}" is not an allowed type`;
|
|
157
137
|
}
|
|
158
138
|
|
|
159
139
|
return null;
|
|
@@ -22,7 +22,12 @@ interface Props {
|
|
|
22
22
|
maxFiles?: number;
|
|
23
23
|
/** Maximum file size in bytes (default: 10MB) */
|
|
24
24
|
maxSizeBytes?: number;
|
|
25
|
-
/**
|
|
25
|
+
/**
|
|
26
|
+
* Accepted file types, as `<input accept>` entries: MIME types
|
|
27
|
+
* (`application/pdf`), wildcards (`image/*`) or extensions (`.dxf`,
|
|
28
|
+
* matched case-insensitively against the file name). A file passes when
|
|
29
|
+
* any entry matches.
|
|
30
|
+
*/
|
|
26
31
|
acceptedTypes?: string[];
|
|
27
32
|
/** Whether upload is disabled */
|
|
28
33
|
disabled?: boolean;
|
package/dist/lib/index.d.ts
CHANGED
|
@@ -116,6 +116,7 @@ export type { PanelEdge, PanelMode, PanelVariant, SnapPoint, CardConfig, Positio
|
|
|
116
116
|
export { cn } from './utils.js';
|
|
117
117
|
export { parseDate, toISOString, formatDate, formatDateTime, isSameDay } from './utils/date.js';
|
|
118
118
|
export { generateId, computeDescribedBy } from './utils/form.js';
|
|
119
|
+
export { matchesAccept, acceptLabel, type AcceptableFile } from './utils/file-accept.js';
|
|
119
120
|
export { formatDuration, formatDurationSpoken, inferMediaKind, parseEmbedUrl, buildEmbedSrc, getEmbedThumbnail, getMediaThumbnail, getMediaLabel, isTimeBasedMedia, isSpatialMedia, isHlsSource, resolveAspectRatio, toMediaSources, HLS_MIME_TYPE, DASH_MIME_TYPE, type ParsedEmbed, type EmbedSrcOptions, } from './utils/media.js';
|
|
120
121
|
export { cloudflareStream, cloudflareStreamUrls, cloudflareStreamThumbnail, cloudflareStreamHost, type CloudflareStreamItemOptions, type CloudflareStreamUrlOptions, type CloudflareStreamUrls, type CloudflareStreamThumbnailOptions, } from './utils/media.js';
|
|
121
122
|
export { matterportTour, buildMatterportSrc, fetchMatterportMetadata, type MatterportTourOptions, type MatterportMetadata, } from './utils/media.js';
|
package/dist/lib/index.js
CHANGED
|
@@ -147,6 +147,7 @@ export { cn } from './utils.js';
|
|
|
147
147
|
export { parseDate, toISOString, formatDate, formatDateTime, isSameDay } from './utils/date.js';
|
|
148
148
|
// Form utilities
|
|
149
149
|
export { generateId, computeDescribedBy } from './utils/form.js';
|
|
150
|
+
export { matchesAccept, acceptLabel } from './utils/file-accept.js';
|
|
150
151
|
// Media utilities
|
|
151
152
|
export { formatDuration, formatDurationSpoken, inferMediaKind, parseEmbedUrl, buildEmbedSrc, getEmbedThumbnail, getMediaThumbnail, getMediaLabel, isTimeBasedMedia, isSpatialMedia, isHlsSource, resolveAspectRatio, toMediaSources, HLS_MIME_TYPE, DASH_MIME_TYPE, } from './utils/media.js';
|
|
152
153
|
// Cloudflare Stream helpers
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Matching and labelling for a list of HTML `accept` entries, as used by
|
|
3
|
+
* `FileUpload`'s `acceptedTypes`. Mirrors the native `<input accept>` rules:
|
|
4
|
+
*
|
|
5
|
+
* - `.dxf` matches a file whose name ends in `.dxf`, case-insensitively
|
|
6
|
+
* (whatever MIME type the browser guessed, often `application/octet-stream` or '')
|
|
7
|
+
* - `image/*` matches any MIME type in the `image` major type
|
|
8
|
+
* - `application/pdf` matches that MIME type exactly, case-insensitively
|
|
9
|
+
*
|
|
10
|
+
* A file is accepted when any entry matches. An empty list accepts everything,
|
|
11
|
+
* as an absent `accept` attribute does.
|
|
12
|
+
*/
|
|
13
|
+
/** The parts of a `File` the matcher reads. */
|
|
14
|
+
export interface AcceptableFile {
|
|
15
|
+
name: string;
|
|
16
|
+
type: string;
|
|
17
|
+
}
|
|
18
|
+
/** Whether `file` matches any of the `accept` entries. */
|
|
19
|
+
export declare function matchesAccept(file: AcceptableFile, accept: readonly string[]): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Readable label for `accept` entries: MIME groups become friendly names
|
|
22
|
+
* (Images, PDF, Videos, Audio), other MIME types their uppercased subtype and
|
|
23
|
+
* extensions their uppercased name (`.dxf` → DXF). Duplicates collapse, so
|
|
24
|
+
* `['application/pdf', '.pdf']` reads "PDF".
|
|
25
|
+
*/
|
|
26
|
+
export declare function acceptLabel(accept: readonly string[]): string;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Matching and labelling for a list of HTML `accept` entries, as used by
|
|
3
|
+
* `FileUpload`'s `acceptedTypes`. Mirrors the native `<input accept>` rules:
|
|
4
|
+
*
|
|
5
|
+
* - `.dxf` matches a file whose name ends in `.dxf`, case-insensitively
|
|
6
|
+
* (whatever MIME type the browser guessed, often `application/octet-stream` or '')
|
|
7
|
+
* - `image/*` matches any MIME type in the `image` major type
|
|
8
|
+
* - `application/pdf` matches that MIME type exactly, case-insensitively
|
|
9
|
+
*
|
|
10
|
+
* A file is accepted when any entry matches. An empty list accepts everything,
|
|
11
|
+
* as an absent `accept` attribute does.
|
|
12
|
+
*/
|
|
13
|
+
function entries(accept) {
|
|
14
|
+
return accept.map((entry) => entry.trim().toLowerCase()).filter(Boolean);
|
|
15
|
+
}
|
|
16
|
+
/** Whether `file` matches any of the `accept` entries. */
|
|
17
|
+
export function matchesAccept(file, accept) {
|
|
18
|
+
const list = entries(accept);
|
|
19
|
+
if (list.length === 0)
|
|
20
|
+
return true;
|
|
21
|
+
const name = file.name.toLowerCase();
|
|
22
|
+
const type = file.type.trim().toLowerCase();
|
|
23
|
+
return list.some((entry) => {
|
|
24
|
+
if (entry.startsWith('.'))
|
|
25
|
+
return name.endsWith(entry) && name.length > entry.length;
|
|
26
|
+
if (entry === '*' || entry === '*/*')
|
|
27
|
+
return true;
|
|
28
|
+
if (entry.endsWith('/*'))
|
|
29
|
+
return type.startsWith(entry.slice(0, -1));
|
|
30
|
+
return type === entry;
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Readable label for `accept` entries: MIME groups become friendly names
|
|
35
|
+
* (Images, PDF, Videos, Audio), other MIME types their uppercased subtype and
|
|
36
|
+
* extensions their uppercased name (`.dxf` → DXF). Duplicates collapse, so
|
|
37
|
+
* `['application/pdf', '.pdf']` reads "PDF".
|
|
38
|
+
*/
|
|
39
|
+
export function acceptLabel(accept) {
|
|
40
|
+
const groups = [];
|
|
41
|
+
const add = (label) => {
|
|
42
|
+
if (!groups.includes(label))
|
|
43
|
+
groups.push(label);
|
|
44
|
+
};
|
|
45
|
+
for (const raw of accept) {
|
|
46
|
+
const type = raw.trim().toLowerCase();
|
|
47
|
+
if (!type)
|
|
48
|
+
continue;
|
|
49
|
+
if (type.startsWith('.'))
|
|
50
|
+
add(type.slice(1).toUpperCase());
|
|
51
|
+
else if (type === '*' || type === '*/*')
|
|
52
|
+
add('Any file');
|
|
53
|
+
else if (type.startsWith('image/'))
|
|
54
|
+
add('Images');
|
|
55
|
+
else if (type === 'application/pdf')
|
|
56
|
+
add('PDF');
|
|
57
|
+
else if (type.startsWith('video/'))
|
|
58
|
+
add('Videos');
|
|
59
|
+
else if (type.startsWith('audio/'))
|
|
60
|
+
add('Audio');
|
|
61
|
+
else {
|
|
62
|
+
const subtype = type.split('/')[1];
|
|
63
|
+
add(subtype ? subtype.toUpperCase() : raw.trim());
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return groups.join(', ');
|
|
67
|
+
}
|