@kedao/finder
Version:
Media manager for Kedao Editor.
303 lines (258 loc) • 7.16 kB
text/typescript
import { UniqueIndex } from './utils/base'
import { compressImage } from './utils/image'
const defaultValidator = () => true
export default class FinderController {
items: any;
uploadFn: any;
validateFn: any;
changeListeners: any;
constructor (props: any = {}) {
this.items = props.items || []
this.uploadFn = props.uploader
this.validateFn = props.validator || defaultValidator
this.changeListeners = []
}
setProps = (props: any = {}) => {
this.items = props.items || this.items || []
this.uploadFn = props.uploader
this.validateFn = props.validator || defaultValidator
};
getMediaItem = (id) => {
return this.items.find((item) => item.id === id)
};
getSelectedItems = () => {
return this.items.filter((item) => item.selected)
};
getItems = () => {
return this.items
};
setItems = (items) => {
this.items =
items.map((item) => ({ ...item, id: item.id.toString() })) || []
this.applyChange()
this.uploadItems()
};
addMediaItem = (item) => {
this.addItems([item])
};
addItems = (items) => {
this.items = [
...this.items,
...items.map((item) => ({ ...item, id: item.id.toString() }))
]
this.applyChange()
this.uploadItems()
};
selectMediaItem = (id) => {
const item = this.getMediaItem(id)
if (item && (item.uploading || item.error)) {
return false
}
this.setMediaItemState(id, {
selected: true
})
};
selectAllItems = () => {
this.items = this.items
.filter((item) => !item.error && !item.uploading)
.map((item) => ({ ...item, selected: true }))
this.applyChange()
};
deselectMediaItem = (id) => {
this.setMediaItemState(id, {
selected: false
})
};
deselectAllItems = () => {
this.items = this.items.map((item) => ({ ...item, selected: false }))
this.applyChange()
};
removeMediaItem = (id) => {
this.items = this.items.filter((item) => item.id !== id)
this.applyChange()
};
removeItems = (ids = []) => {
this.items = this.items.filter((item) => !ids.includes(item.id))
this.applyChange()
};
removeSelectedItems = () => {
this.items = this.items.filter((item) => !item.selected)
this.applyChange()
};
removeErrorItems = () => {
this.items = this.items.filter((item) => !item.error)
this.applyChange()
};
removeAllItems = () => {
this.items = []
this.applyChange()
};
setMediaItemState = (id, state) => {
this.items = this.items.map((item) =>
item.id === id ? { ...item, ...state } : item
)
this.applyChange()
};
reuploadErrorItems = () => {
this.uploadItems(true)
};
uploadItems = (ignoreError = false) => {
this.items.forEach((item, index) => {
if (item.uploading || item.url) {
return false
}
if (!ignoreError && item.error) {
return false
}
if (item.type === 'IMAGE') {
this.createThumbnail(item)
this.uploadFn = this.uploadFn || this.createInlineImage
} else if (!this.uploadFn) {
this.setMediaItemState(item.id, { error: 1 })
return false
}
this.setMediaItemState(item.id, {
uploading: true,
uploadProgress: 0,
error: 0
})
this.uploadFn({
id: item.id,
file: item.file,
success: (res) => {
this.handleUploadSuccess(item.id, res)
},
progress: (progress) => {
this.setMediaItemState(item.id, {
uploading: true,
uploadProgress: progress
})
},
error: () => {
this.setMediaItemState(item.id, {
uploading: false,
error: 2
})
}
})
})
};
createThumbnail = ({ id, file }) => {
compressImage(URL.createObjectURL(file), 226, 226).then((result: any) => {
this.setMediaItemState(id, { thumbnail: result.url })
}).catch(console.error)
};
createInlineImage = (param) => {
compressImage(URL.createObjectURL(param.file), 1280, 800)
.then((result: any) => {
param.success({ url: result.url })
})
.catch((error) => {
param.error(error)
})
};
handleUploadSuccess = (id, data) => {
this.setMediaItemState(id, {
...data,
file: null,
uploadProgress: 1,
uploading: false,
selected: false
})
const item = this.getMediaItem(data.id || id)
item.onReady?.(item)
};
applyChange = () => {
this.changeListeners.forEach(({ callback }) => callback(this.items))
};
uploadImage = (file, callback) => {
const fileId = new Date().getTime() + '_' + UniqueIndex()
this.addMediaItem({
type: 'IMAGE',
id: fileId,
file: file,
name: fileId,
size: file.size,
uploadProgress: 0,
uploading: false,
selected: false,
error: 0,
onReady: callback
})
};
uploadImageRecursively = (files, callback, index = 0) => {
if (files[index] && files[index].type.indexOf('image') > -1) {
this.uploadImage(files[index], (image) => {
callback?.(image)
index < files.length - 1 &&
this.uploadImageRecursively(files, callback, index + 1)
})
} else {
index < files.length - 1 &&
this.uploadImageRecursively(files, callback, index + 1)
}
};
addResolvedFiles = (param, index, accepts) => {
const data: any = {
id: new Date().getTime() + '_' + UniqueIndex(),
file: param.files[index],
name: param.files[index].name,
size: param.files[index].size,
uploadProgress: 0,
uploading: false,
selected: false,
error: 0,
onReady: (item) => {
param.onItemReady?.(item)
}
}
if (param.files[index].type.indexOf('image/') === 0 && accepts.image) {
data.type = 'IMAGE'
this.addMediaItem(data)
} else if (
param.files[index].type.indexOf('video/') === 0 &&
accepts.video
) {
data.type = 'VIDEO'
this.addMediaItem(data)
} else if (
param.files[index].type.indexOf('audio/') === 0 &&
accepts.audio
) {
data.type = 'AUDIO'
this.addMediaItem(data)
}
setTimeout(() => {
this.resolveFiles(param, index + 1, accepts)
}, 60)
};
resolveFiles = (param, index, accepts) => {
if (index < param.files.length) {
const validateResult = this.validateFn(param.files[index])
if (validateResult instanceof Promise) {
validateResult
.then(() => {
this.addResolvedFiles(param, index, accepts)
})
.catch(console.error)
} else if (validateResult) {
this.addResolvedFiles(param, index, accepts)
}
} else {
param.onAllReady?.()
}
};
onChange = (callback) => {
const listenerId = UniqueIndex()
this.changeListeners.push({
id: listenerId,
callback: callback
})
return listenerId
};
offChange = (listenerId) => {
this.changeListeners = this.changeListeners.filter(
({ id }) => id !== listenerId
)
};
}