@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.
@@ -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 (accept && !matchesAccept(file, accept)) {
46
- return { reason: 'type' }
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 (mimeType.startsWith('image/')) return 'i-lucide:image'
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 || items.length === 0) return []
130
+ if (!items?.length) return []
96
131
 
97
132
  const root = []
98
- /** @type {Map<string, Record<string, any>>} */
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