@rokkit/ui 1.0.0-next.136 → 1.0.0-next.138
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/package.json +1 -1
- package/src/components/Carousel.svelte +0 -1
- package/src/components/FloatingAction.svelte +66 -37
- package/src/components/FloatingNavigation.svelte +78 -68
- package/src/components/LazyTree.svelte +1 -2
- package/src/components/List.svelte +0 -1
- package/src/components/Menu.svelte +0 -1
- package/src/components/MultiSelect.svelte +30 -43
- package/src/components/Range.svelte +52 -62
- package/src/components/Rating.svelte +26 -16
- package/src/components/SearchFilter.svelte +1 -1
- package/src/components/Select.svelte +72 -52
- package/src/components/Switch.svelte +10 -14
- package/src/components/Table.svelte +10 -13
- package/src/components/Tabs.svelte +0 -2
- package/src/components/Toolbar.svelte +9 -13
- package/src/components/Tree.svelte +0 -1
- package/src/components/UploadTarget.svelte +13 -15
- package/src/utils/palette.ts +38 -55
- package/src/utils/shiki.ts +16 -24
- package/src/utils/upload.js +52 -38
package/src/utils/upload.js
CHANGED
|
@@ -41,13 +41,17 @@ export function matchesAccept(file, accept) {
|
|
|
41
41
|
* @param {{ accept?: string, maxSize?: number }} constraints
|
|
42
42
|
* @returns {true | { reason: 'type' | 'size' }}
|
|
43
43
|
*/
|
|
44
|
+
function hasTypeError(file, accept) {
|
|
45
|
+
return accept && !matchesAccept(file, accept)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function hasSizeError(file, maxSize) {
|
|
49
|
+
return maxSize !== undefined && file.size > maxSize
|
|
50
|
+
}
|
|
51
|
+
|
|
44
52
|
export function validateFile(file, { accept, maxSize } = {}) {
|
|
45
|
-
if (
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
if (maxSize !== undefined && file.size > maxSize) {
|
|
49
|
-
return { reason: 'size' }
|
|
50
|
-
}
|
|
53
|
+
if (hasTypeError(file, accept)) return { reason: 'type' }
|
|
54
|
+
if (hasSizeError(file, maxSize)) return { reason: 'size' }
|
|
51
55
|
return true
|
|
52
56
|
}
|
|
53
57
|
|
|
@@ -56,16 +60,26 @@ export function validateFile(file, { accept, maxSize } = {}) {
|
|
|
56
60
|
* @param {string | null | undefined} mimeType
|
|
57
61
|
* @returns {string}
|
|
58
62
|
*/
|
|
63
|
+
const MIME_PREFIX_ICONS = [
|
|
64
|
+
['image/', 'i-lucide:image'],
|
|
65
|
+
['video/', 'i-lucide:video'],
|
|
66
|
+
['audio/', 'i-lucide:music'],
|
|
67
|
+
['text/', 'i-lucide:file-text'],
|
|
68
|
+
['application/pdf', 'i-lucide:file-text']
|
|
69
|
+
]
|
|
70
|
+
|
|
71
|
+
function getPrefixIcon(mimeType) {
|
|
72
|
+
for (const [prefix, icon] of MIME_PREFIX_ICONS) {
|
|
73
|
+
if (mimeType.startsWith(prefix)) return icon
|
|
74
|
+
}
|
|
75
|
+
return null
|
|
76
|
+
}
|
|
77
|
+
|
|
59
78
|
export function inferIcon(mimeType) {
|
|
60
79
|
if (!mimeType) return 'i-lucide:file'
|
|
61
|
-
|
|
62
|
-
if (
|
|
63
|
-
if (mimeType.startsWith('video/')) return 'i-lucide:video'
|
|
64
|
-
if (mimeType.startsWith('audio/')) return 'i-lucide:music'
|
|
65
|
-
if (mimeType === 'application/pdf') return 'i-lucide:file-text'
|
|
66
|
-
if (mimeType.startsWith('text/')) return 'i-lucide:file-text'
|
|
80
|
+
const prefixIcon = getPrefixIcon(mimeType)
|
|
81
|
+
if (prefixIcon) return prefixIcon
|
|
67
82
|
if (ARCHIVE_TYPES.has(mimeType)) return 'i-lucide:archive'
|
|
68
|
-
|
|
69
83
|
return 'i-lucide:file'
|
|
70
84
|
}
|
|
71
85
|
|
|
@@ -91,40 +105,40 @@ export function formatSize(bytes) {
|
|
|
91
105
|
* @param {string} childrenField - field name for children arrays (e.g. 'children')
|
|
92
106
|
* @returns {Array<Record<string, any>>}
|
|
93
107
|
*/
|
|
108
|
+
function resolveFolder(ctx, parent, segment, fullPath) {
|
|
109
|
+
let folder = ctx.folderMap.get(fullPath)
|
|
110
|
+
if (!folder) {
|
|
111
|
+
folder = { text: segment, [ctx.childrenField]: [] }
|
|
112
|
+
ctx.folderMap.set(fullPath, folder)
|
|
113
|
+
parent.push(folder)
|
|
114
|
+
}
|
|
115
|
+
return folder[ctx.childrenField]
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function walkSegments(ctx, item, rawPath, root) {
|
|
119
|
+
const segments = rawPath.split('/').filter(Boolean)
|
|
120
|
+
let parent = root
|
|
121
|
+
let fullPath = ''
|
|
122
|
+
for (const segment of segments) {
|
|
123
|
+
fullPath = fullPath ? `${fullPath}/${segment}` : segment
|
|
124
|
+
parent = resolveFolder(ctx, parent, segment, fullPath)
|
|
125
|
+
}
|
|
126
|
+
parent.push(item)
|
|
127
|
+
}
|
|
128
|
+
|
|
94
129
|
export function groupByPath(items, pathField, childrenField) {
|
|
95
|
-
if (!items
|
|
130
|
+
if (!items?.length) return []
|
|
96
131
|
|
|
97
132
|
const root = []
|
|
98
|
-
|
|
99
|
-
const folderMap = new Map()
|
|
133
|
+
const ctx = { folderMap: new Map(), childrenField }
|
|
100
134
|
|
|
101
135
|
for (const item of items) {
|
|
102
|
-
const rawPath = item[pathField]
|
|
136
|
+
const rawPath = item[pathField]
|
|
103
137
|
if (!rawPath) {
|
|
104
138
|
root.push(item)
|
|
105
139
|
continue
|
|
106
140
|
}
|
|
107
|
-
|
|
108
|
-
// Split path into segments, filtering empty strings from trailing slashes
|
|
109
|
-
const segments = rawPath.split('/').filter(Boolean)
|
|
110
|
-
let parent = root
|
|
111
|
-
let fullPath = ''
|
|
112
|
-
|
|
113
|
-
for (let i = 0; i < segments.length; i++) {
|
|
114
|
-
const segment = segments[i]
|
|
115
|
-
fullPath = fullPath ? `${fullPath}/${segment}` : segment
|
|
116
|
-
|
|
117
|
-
let folder = folderMap.get(fullPath)
|
|
118
|
-
if (!folder) {
|
|
119
|
-
folder = { text: segment, [childrenField]: [] }
|
|
120
|
-
folderMap.set(fullPath, folder)
|
|
121
|
-
parent.push(folder)
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
parent = folder[childrenField]
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
parent.push(item)
|
|
141
|
+
walkSegments(ctx, item, rawPath, root)
|
|
128
142
|
}
|
|
129
143
|
|
|
130
144
|
return root
|