@cocreate/file 1.0.0 → 1.1.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.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ # [1.1.0](https://github.com/CoCreate-app/CoCreate-file/compare/v1.0.0...v1.1.0) (2023-06-08)
2
+
3
+
4
+ ### Features
5
+
6
+ * add template_id to fileInput and render selected files using template and placeholders ([ca29280](https://github.com/CoCreate-app/CoCreate-file/commit/ca292805b3b3d71e95a022cb249082ace540d33a))
7
+
1
8
  # 1.0.0 (2023-06-06)
2
9
 
3
10
 
package/demo/index.html CHANGED
@@ -13,23 +13,28 @@
13
13
  </head>
14
14
  <body>
15
15
  <form>
16
- <input type="file" class="fileInput" />
17
- <button actions="uploadFiles">upload</button>
16
+ <input type="file" />
17
+ <button actions="upload">upload</button>
18
18
  </form>
19
19
 
20
20
  <form>
21
- <input type="file" class="fileInput" multiple />
22
- <button actions="uploadFiles">upload</button>
21
+ <input type="file" multiple />
22
+ <button actions="upload">upload</button>
23
23
  </form>
24
24
  <form>
25
- <input type="file" class="fileInput" directory />
26
- <button actions="uploadFiles">upload</button>
25
+ <input type="file" directory />
26
+ <button actions="upload">upload</button>
27
27
  </form>
28
28
  <form>
29
- <input type="file" class="fileInput" directory multiple />
30
- <button actions="uploadFiles">upload</button>
29
+ <input type="file" directory multiple template="file-preview" />
30
+ <div type="file" directory multiple>
31
+ // adds input with display none
32
+ </div>
33
+ <button actions="upload">upload</button>
31
34
  </form>
32
-
35
+ <div template="file-preview">
36
+ <span>{{file.name}}</span>
37
+ </div>
33
38
  <script src="../dist/CoCreate-file.js"></script>
34
39
  <script src="https://cdn.cocreate.app/latest/CoCreate.min.js"></script>
35
40
  </body>
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@cocreate/file",
3
- "version": "1.0.0",
4
- "description": "This is a configurable headless file uploader that utilizes HTML5 attributes for easy customization. With this module, users can easily upload files to a server without requiring a formal UI or browser interaction. By leveraging HTML5 attributes, it's easy to customize and fine-tune the behavior of the uploader to fit specific needs. This uploader is perfect for developers looking to implement file uploads in a headless environment.",
3
+ "version": "1.1.0",
4
+ "description": "A headless file uploader that uses HTML5 attributes for customization. Allows easy upload of files to server.",
5
5
  "keywords": [
6
6
  "file",
7
7
  "chain-functions",
@@ -28,7 +28,8 @@
28
28
  "start": "npx webpack --config webpack.config.js",
29
29
  "build": "NODE_ENV=production npx webpack --config webpack.config.js",
30
30
  "dev": "npx webpack --config webpack.config.js --watch",
31
- "docs": "node ./node_modules/@cocreate/docs/src/index.js"
31
+ "docs": "node ./node_modules/@cocreate/docs/src/index.js",
32
+ "postinstall": "node ./node_modules/@cocreate/cli/check-coc.js"
32
33
  },
33
34
  "repository": {
34
35
  "type": "git",
@@ -48,6 +49,7 @@
48
49
  "devDependencies": {
49
50
  "@babel/core": "^7.9.6",
50
51
  "@babel/preset-env": "^7.9.6",
52
+ "@cocreate/cli": "^1.29.3",
51
53
  "babel-loader": "^8.1.0",
52
54
  "clean-webpack-plugin": "^3.0.0",
53
55
  "file-loader": "^6.2.0",
@@ -60,7 +62,7 @@
60
62
  },
61
63
  "dependencies": {
62
64
  "@cocreate/actions": "^1.8.18",
63
- "@cocreate/docs": "^1.7.15",
64
- "@cocreate/utils": "^1.21.1"
65
+ "@cocreate/docs": "^1.8.2",
66
+ "@cocreate/utils": "^1.21.3"
65
67
  }
66
68
  }
package/src/index.js CHANGED
@@ -1,253 +1,14 @@
1
- import actions from '@cocreate/actions'
2
-
3
- let inputs = new Map();
4
-
5
- function handleFileInputChange(event) {
6
- const input = event.target;
7
- const files = input.files;
8
- let selected = inputs.get(input) || []
9
- selected.push(...files)
10
- inputs.set(input, selected);
11
- console.log("Files selected:", files);
12
- }
13
-
14
- async function selectFile(event) {
15
- event.preventDefault()
16
- const input = event.target;
17
- let selected = inputs.get(input) || []
18
- try {
19
- const multiple = input.multiple
20
- const selectedFiles = await window.showOpenFilePicker({ multiple });
21
-
22
- for (const handle of selectedFiles) {
23
- selected.push(handle)
24
- }
25
-
26
- if (selected.length) {
27
- inputs.set(input, selected);
28
- console.log("Files selected:", selected);
29
- }
30
-
31
- } catch (error) {
32
- if (error.name !== 'AbortError') {
33
- console.error("Error selecting files:", error);
34
- }
35
- }
36
- }
37
-
38
- async function selectDirectory(event) {
39
- event.preventDefault()
40
- const input = event.target;
41
- let selected = inputs.get(input) || []
42
-
43
- try {
44
- const multiple = input.multiple
45
- const handle = await window.showDirectoryPicker();
46
- selected.push(handle)
47
-
48
- if (selected.length) {
49
- inputs.set(input, selected);
50
- console.log("Directory selected:", selected);
51
- }
52
- } catch (error) {
53
- if (error.name !== 'AbortError') {
54
- console.error("Error selecting directory:", error);
55
- }
56
- }
57
- }
58
-
59
- async function getNewFileHandle() {
60
- // const options = {
61
- // types: [
62
- // {
63
- // description: 'Text Files',
64
- // accept: {
65
- // 'text/plain': ['.txt'],
66
- // },
67
- // },
68
- // ],
69
- // };
70
- const handle = await window.showSaveFilePicker(options);
71
- return handle;
72
- }
73
-
74
- async function generateFileObjects(fileInputs, isObject) {
75
- const files = [];
76
-
77
- if (!Array.isArray(fileInputs))
78
- fileInputs = [fileInputs]
79
-
80
- for (let input of fileInputs) {
81
- const selected = inputs.get(input) || []
82
-
83
- for (let i = 0; i < selected.length; i++) {
84
- let file
85
- if (selected[i] instanceof FileSystemDirectoryHandle) {
86
- // The object is an instance of FileSystemFileHandle
87
- const handles = await getFilesFromDirectory(selected[i], selected[i].name)
88
- for (let handle of handles) {
89
- file = await handle.getFile();
90
- file.directory = handle.directory
91
- file.parentDirectory = handle.parentDirectory
92
- file.path = handle.path
93
-
94
- if (isObject)
95
- files.push(await createFileObject(file))
96
- else
97
- files.push(file)
98
- }
99
- } else {
100
- if (selected[i] instanceof FileSystemFileHandle) {
101
- // The object is an instance of FileSystemFileHandle
102
- file = await selected[i].getFile();
103
- } else {
104
- // The object is not an instance of FileSystemFileHandle
105
- console.log("It's not a FileSystemFileHandle object");
106
- file = selected[i]
107
- }
108
- const fileUrl = URL.createObjectURL(file);
109
- console.log(fileUrl);
110
- if (isObject = true)
111
- files.push(await createFileObject(file))
112
- else
113
- files.push(file)
114
- }
115
-
116
- }
117
- }
118
-
119
- return files
120
- }
121
-
122
- async function getFilesFromDirectory(handle, name) {
123
- let files = [];
124
- for await (const entry of handle.values()) {
125
- if (entry.kind === 'file') {
126
- entry.directory = '/' + name
127
- entry.parentDirectory = name.split("/").pop();
128
- entry.path = '/' + name + '/' + entry.name
129
- if (!entry.webkitRelativePath)
130
- entry.webkitRelativePath = name
131
-
132
- files.push(entry);
133
- } else if (entry.kind === 'directory') {
134
- const entries = await getFilesFromDirectory(entry, name + '/' + entry.name);
135
- files = files.concat(entries);
136
- }
137
- }
138
- return files;
139
- }
140
-
141
- // This function creates a file object from the given file
142
- function createFileObject(file) {
143
- // Return a new promise that resolves the file object
144
- return new Promise((resolve) => {
145
- // Create a FileReader instance to read the file
146
- const reader = new FileReader();
147
- // Split the file type into an array
148
- const fileType = file.type.split('/');
149
- let readAs;
150
-
151
- // Check if the file type is an image
152
- if (['jpg', 'jpeg', 'png', 'gif', 'bmp'].includes(fileType[1])
153
- || fileType[0] === 'image') {
154
- readAs = 'readAsDataURL';
155
- }
156
- // Check if the file type is a video
157
- else if (['mp4', 'avi', 'mov', 'mpeg', 'flv'].includes(fileType[1])
158
- || fileType[0] === 'video') {
159
- readAs = 'readAsDataURL';
160
- }
161
- // Check if the file type is an audio
162
- else if (['mp3', 'wav', 'wma', 'aac', 'ogg'].includes(fileType[1])
163
- || fileType[0] === 'audio') { // updated condition
164
- readAs = 'readAsDataURL';
165
- }
166
- // Check if the file type is a pdf
167
- else if (fileType[1] === 'pdf') {
168
- readAs = 'readAsDataURL';
169
- }
170
- // Check if the file type is a document
171
- else if (['doc', 'msword', 'docx', 'xlsx', 'pptx'].includes(fileType[1])) {
172
- readAs = 'readAsBinaryString';
173
- }
174
- // Otherwise, assume the file type is text
175
- else {
176
- readAs = 'readAsText';
177
- }
178
-
179
- // Read the file based on the file type
180
- reader[readAs](file);
181
- // When the file is loaded, resolve the file object
182
- reader.onload = () => {
183
- let src = reader.result;
184
- // If the file type is a document, convert it to base64 encoding
185
- if (['doc', 'msword', 'docx', 'xlsx', 'pptx'].includes(fileType)) {
186
- src = btoa(src);
187
- }
188
-
189
- // Resolve the file object
190
- resolve({
191
- name: file.name,
192
- path: file.path,
193
- src,
194
- directory: file.directory,
195
- parentDirectory: file.parentDirectory,
196
- size: file.size,
197
- "content-type": file.type,
198
- lastModified: file.lastModified,
199
- lastModifiedDate: file.lastModifiedDate,
200
- modified: { on: file.lastModifiedDate, by: "unknown" },
201
- public: "true"
202
- });
203
- };
204
- });
205
- }
206
-
207
- const fileInputs = document.querySelectorAll('input[type="file"]')
208
-
209
- for (let i = 0; i < fileInputs.length; i++) {
210
- if (fileInputs[i].hasAttribute('directory')) {
211
- if (window.showDirectoryPicker)
212
- fileInputs[i].addEventListener("click", selectDirectory);
213
- else if ('webkitdirectory' in fileInputs[i]) {
214
- fileInputs[i].webkitdirectory = true
215
- fileInputs[i].addEventListener("change", handleFileInputChange)
216
- } else
217
- console.error("Directory selection not supported in this browser.");
218
- } else if (window.showOpenFilePicker)
219
- fileInputs[i].addEventListener("click", selectFile);
220
- else
221
- fileInputs[i].addEventListener("change", handleFileInputChange);
222
-
223
- }
224
-
225
- async function fileAction(btn, params, action) {
226
- const form = btn.closest('form')
227
- let inputs = form.querySelectorAll('input[type="file"]')
228
- let fileObjects = await generateFileObjects(Array.from(inputs), true)
229
-
230
- console.log('fileObjects', fileObjects)
231
- document.dispatchEvent(new CustomEvent(action, {
232
- detail: {}
233
- }));
234
-
235
- }
236
-
237
- async function getFiles(inputs) {
238
- let files = await generateFileObjects(fileInputs)
239
- return files
240
- }
241
- async function getObjects(inputs) {
242
- let objects = await generateFileObjects(fileInputs, true)
243
- return objects
244
- }
245
-
246
- actions.init({
247
- name: "uploadFiles",
248
- callback: (btn, params) => {
249
- fileAction(btn, params, "uploadFiles")
250
- }
251
- })
252
-
253
- export default { getFiles, getObjects }
1
+ (function (root, factory) {
2
+ if (typeof define === 'function' && define.amd) {
3
+ define(["./client"], function (CoCreateFile) {
4
+ return factory(CoCreateFile)
5
+ });
6
+ } else if (typeof module === 'object' && module.exports) {
7
+ const CoCreateFile = require("./server.js")
8
+ module.exports = factory(CoCreateFile);
9
+ } else {
10
+ root.returnExports = factory(root["./client.js"]);
11
+ }
12
+ }(typeof self !== 'undefined' ? self : this, function (CoCreateFile) {
13
+ return CoCreateFile;
14
+ }));
package/src/upload.js DELETED
@@ -1,341 +0,0 @@
1
- const crud = require('@cocreate/crud-client')
2
- const mime = require('mime-types')
3
- const fs = require('fs');
4
- const path = require('path');
5
-
6
-
7
- module.exports = async function upload(repos, args) {
8
-
9
- let CoCreateConfig;
10
- let configFile = path.resolve(process.cwd(), 'CoCreate.config.js');
11
- if (fs.existsSync(configFile)) {
12
- CoCreateConfig = require(configFile);
13
- } else {
14
- console.log('CoCreate.config.js could not be found.')
15
- process.exit()
16
- }
17
-
18
- let { config, directories, sources } = CoCreateConfig;
19
-
20
- if (!config)
21
- config = process.env.config || {}
22
- if (!config.organization_id)
23
- config.organization_id = process.env.organization_id
24
- if (!config.key)
25
- config.key = process.env.key
26
- if (!config.host)
27
- config.host = process.env.host
28
-
29
- if (!config.organization_id || !config.key || !config.host) {
30
- config = await require('./config.js')(config)
31
- }
32
-
33
- if (!config.organization_id || !config.host || !config.key && (!config.password || config.email)) {
34
- console.log('One or more required config params could not be found')
35
- process.exit()
36
- }
37
-
38
-
39
- crud.socket.create(config)
40
- config.broadcast = false
41
-
42
- if (config.email && config.password) {
43
- let request = {
44
- collection: 'users',
45
- filter: {
46
- query: [
47
- { name: 'email', value: config.email, operator: '$eq' },
48
- { name: 'password', value: config.password, operator: '$eq' }
49
- ]
50
- }
51
- }
52
-
53
- let response = await crud.socket.send('signIn', request)
54
- let { success, token } = response;
55
-
56
- if (success) {
57
- console.log('succesful sign in')
58
- // apply token to socket
59
- } else {
60
- console.log('The email or password you entered is incorrect')
61
- process.exit()
62
-
63
- }
64
-
65
- }
66
-
67
- console.log('Uploading files...')
68
-
69
- /**
70
- * Store files by config directories
71
- **/
72
- let errorLog = [];
73
-
74
- async function runDirectories() {
75
- for (const directory of directories) {
76
- const entry = directory.entry
77
- const exclude = directory.exclude
78
- await runFiles(directory, entry, exclude)
79
- }
80
- return
81
- }
82
-
83
- async function runFiles(directory, entry, exclude, parentDirectory = '') {
84
- let files = fs.readdirSync(entry);
85
-
86
- for (let file of files) {
87
- if (exclude && exclude.includes(file)) continue
88
-
89
- let isDirectory = fs.existsSync(`${entry}/${file}`) && fs.lstatSync(`${entry}/${file}`).isDirectory();
90
- let name = file
91
- let source = ''
92
- let directoryName = parentDirectory || '';
93
- let parentDirectoryOnly = parentDirectory || '';
94
- let index = parentDirectoryOnly.lastIndexOf('/') + 1
95
- if (parentDirectoryOnly && index) {
96
- parentDirectoryOnly = parentDirectoryOnly.substring(index)
97
- }
98
- let mimeType = mime.lookup(`${file}`)
99
- let pathName = '';
100
-
101
- if (!directoryName && directory.document && directory.document.directory)
102
- directoryName = directory.document.directory.replace('{{directory}}', '').trim()
103
- else if (!directoryName)
104
- directoryName = '/'
105
-
106
- if (exclude && exclude.includes(directoryName)) continue
107
-
108
- if (directoryName.endsWith("/"))
109
- pathName = directoryName + name
110
- else if (directoryName)
111
- pathName = directoryName + '/' + name
112
- else
113
- pathName = '/' + name
114
-
115
- if (exclude && exclude.includes(pathName)) continue
116
-
117
- if (isDirectory)
118
- mimeType = "text/directory"
119
- else
120
- source = getSource(`${entry}/${file}`, mimeType)
121
-
122
- let values = {
123
- '{{name}}': name,
124
- '{{source}}': source,
125
- '{{directory}}': directoryName,
126
- '{{parentDirectory}}': parentDirectoryOnly,
127
- '{{path}}': pathName,
128
- '{{content-type}}': mimeType
129
- }
130
-
131
- let document = { ...directory.document }
132
- if (!document.name)
133
- document.name = "{{name}}"
134
- if (!document.src)
135
- document.src = "{{source}}"
136
- if (!document.directory)
137
- document.directory = "/{{directory}}"
138
- if (!document.parentDirectory)
139
- document.parentDirectory = "{{parentDirectory}}"
140
- if (!document.path)
141
- document.path = "{{path}}"
142
- if (!document["content-type"])
143
- document["content-type"] = '{{content-type}}'
144
- if (!document.public && document.public != false && document.public != 'false')
145
- document.public = 'true'
146
-
147
- let object = {
148
- collection: directory.collection || 'files',
149
- document
150
- }
151
- for (const key of Object.keys(directory.document)) {
152
- if (typeof directory.document[key] == 'string') {
153
-
154
- let variables = directory.document[key].match(/{{([A-Za-z0-9_.,\[\]\-\/ ]*)}}/g);
155
- if (variables) {
156
- for (let variable of variables) {
157
- if (variable == '{{directory}}') {
158
- if (parentDirectory)
159
- object.document[key] = values[variable]
160
- else
161
- object.document[key] = object.document[key].replace(variable, '');
162
- }
163
- else if (isDirectory && variable == '{{source}}')
164
- delete object.document[key]
165
- else
166
- object.document[key] = object.document[key].replace(variable, values[variable]);
167
- }
168
- }
169
-
170
- }
171
- }
172
-
173
- if (!object.document._id)
174
- object.filter = {
175
- query: [{ name: 'path', value: pathName, operator: '$eq' }]
176
- }
177
-
178
- response = await runStore(object);
179
- if (response.error)
180
- errorLog.push(response.error)
181
-
182
- if (isDirectory && pathName) {
183
- let newEntry
184
- if (entry.endsWith("/"))
185
- newEntry = entry + name
186
- else
187
- newEntry = entry + '/' + name
188
-
189
- await runFiles(directory, newEntry, exclude, pathName)
190
- }
191
- }
192
- if (errorLog.length)
193
- console.log(...errorLog)
194
-
195
- }
196
-
197
-
198
- function getSource(path, mimeType) {
199
- let readType = 'utf8'
200
- if (/^(image|audio|video)\/[-+.\w]+/.test(mimeType))
201
- readType = 'base64'
202
-
203
- let binary = fs.readFileSync(path);
204
- let content = new Buffer.from(binary).toString(readType);
205
-
206
- return content
207
- }
208
-
209
- /**
210
- * Store files by config sources
211
- **/
212
- async function runSources() {
213
- let updatedSources = [];
214
-
215
- for (let i = 0; i < sources.length; i++) {
216
- const { collection, document } = sources[i];
217
-
218
- let source = { ...sources[i] };
219
- let keys = new Map()
220
- let response = {};
221
-
222
- try {
223
- if (collection) {
224
- if (!document)
225
- document = {};
226
- else
227
- for (const key of Object.keys(document)) {
228
- if (typeof document[key] != 'string')
229
- continue
230
-
231
- let variables = document[key].match(/{{([A-Za-z0-9_.,\[\]\-\/ ]*)}}/g);
232
- if (variables) {
233
- keys.set(key, `${document[key]}`)
234
- let value = ""
235
- for (let variable of variables) {
236
- let entry = /{{\s*([\w\W]+)\s*}}/g.exec(variable);
237
- entry = entry[1].trim()
238
- if (entry) {
239
- if (!fs.existsSync(entry))
240
- continue
241
-
242
- let read_type = 'utf8'
243
- let mime_type = mime.lookup(entry) || 'text/html';
244
- if (/^(image|audio|video)\/[-+.\w]+/.test(mime_type)) {
245
- read_type = 'base64'
246
- }
247
-
248
- let binary = fs.readFileSync(entry);
249
- let content = new Buffer.from(binary).toString(read_type);
250
- if (content)
251
- value += content
252
- // document[key] = document[key].replace(variable, content);
253
- }
254
- }
255
- document[key] = value
256
- }
257
-
258
- }
259
-
260
- let data = { collection, document }
261
- if (!document._id && document.path)
262
- data.filter = {
263
- query: [{ name: 'path', value: document.path, operator: '$eq' }]
264
- }
265
-
266
- response = await runStore(data);
267
- }
268
- } catch (err) {
269
- console.log(err)
270
- process.exit()
271
- }
272
- if (response.document && response.document[0] && response.document[0]._id) {
273
- for (const [key, value] of keys) {
274
- source.document[key] = value
275
- }
276
- source.document._id = response.document[0]._id
277
- } else {
278
- console.log('_id could not be found')
279
- process.exit()
280
- }
281
-
282
- updatedSources.push(source)
283
- }
284
-
285
- return updatedSources
286
- }
287
-
288
-
289
- async function runStore(data) {
290
- try {
291
- let response;
292
- if (!data.document._id && !data.filter) {
293
- response = await crud.createDocument({
294
- ...config,
295
- ...data
296
- })
297
- } else {
298
- response = await crud.updateDocument({
299
- ...config,
300
- ...data,
301
- upsert: true
302
- })
303
- }
304
- if (response) {
305
- return response;
306
- }
307
- } catch (err) {
308
- console.log(err);
309
- return null;
310
- }
311
- }
312
-
313
- async function run() {
314
- if (directories)
315
- await runDirectories()
316
-
317
- if (sources) {
318
- let sources = await runSources()
319
- let newConfig = { ...CoCreateConfig }
320
- if (directories)
321
- newConfig.directories = directories
322
-
323
- newConfig.sources = sources
324
-
325
- delete newConfig.config.url
326
- delete newConfig.config.broadcast
327
- let write_str = JSON.stringify(newConfig, null, 4)
328
- write_str = "module.exports = " + write_str;
329
-
330
- fs.writeFileSync(configFile, write_str);
331
- }
332
-
333
- console.log('upload complete!');
334
-
335
- setTimeout(function () {
336
- process.exit()
337
- }, 2000)
338
- }
339
-
340
- run()
341
- }