@knowark/componarkjs 1.7.3
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/Makefile +49 -0
- package/README.md +47 -0
- package/knowarkjs.code-workspace +29 -0
- package/lib/base/component/README.rst +113 -0
- package/lib/base/component/component.js +115 -0
- package/lib/base/component/component.test.js +327 -0
- package/lib/base/component/index.js +3 -0
- package/lib/base/index.js +1 -0
- package/lib/base/styles/index.js +3 -0
- package/lib/base/styles/styles.js +320 -0
- package/lib/base/utils/define.js +21 -0
- package/lib/base/utils/define.test.js +62 -0
- package/lib/base/utils/format.js +24 -0
- package/lib/base/utils/format.test.js +19 -0
- package/lib/base/utils/helpers.js +96 -0
- package/lib/base/utils/helpers.test.js +154 -0
- package/lib/base/utils/index.js +5 -0
- package/lib/base/utils/slots.js +18 -0
- package/lib/base/utils/slots.test.js +52 -0
- package/lib/base/utils/uuid.js +9 -0
- package/lib/base/utils/uuid.test.js +19 -0
- package/lib/components/audio/README.md +22 -0
- package/lib/components/audio/components/audio.js +103 -0
- package/lib/components/audio/components/audio.test.js +127 -0
- package/lib/components/audio/index.js +1 -0
- package/lib/components/audio/styles/ark.css.js +83 -0
- package/lib/components/audio/styles/index.js +2 -0
- package/lib/components/camera/README.md +64 -0
- package/lib/components/camera/components/camera.js +85 -0
- package/lib/components/camera/components/camera.test.js +104 -0
- package/lib/components/camera/index.js +1 -0
- package/lib/components/camera/styles/ark.css.js +17 -0
- package/lib/components/camera/styles/index.js +2 -0
- package/lib/components/capture/components/capture.js +54 -0
- package/lib/components/capture/components/capture.test.js +112 -0
- package/lib/components/capture/index.js +1 -0
- package/lib/components/droparea/README.md +51 -0
- package/lib/components/droparea/components/droparea-preview.js +159 -0
- package/lib/components/droparea/components/droparea-preview.test.js +105 -0
- package/lib/components/droparea/components/droparea.js +165 -0
- package/lib/components/droparea/components/droparea.test.js +320 -0
- package/lib/components/droparea/index.js +1 -0
- package/lib/components/droparea/styles/ark.css.js +235 -0
- package/lib/components/droparea/styles/index.js +3 -0
- package/lib/components/emit/components/emit.js +33 -0
- package/lib/components/emit/components/emit.test.js +138 -0
- package/lib/components/emit/index.js +1 -0
- package/lib/components/index.js +9 -0
- package/lib/components/list/README.md +103 -0
- package/lib/components/list/components/item.test.js +93 -0
- package/lib/components/list/components/list.item.js +22 -0
- package/lib/components/list/components/list.js +96 -0
- package/lib/components/list/components/list.test.js +267 -0
- package/lib/components/list/index.js +2 -0
- package/lib/components/paginator/README.md +32 -0
- package/lib/components/paginator/components/paginator.js +110 -0
- package/lib/components/paginator/components/paginator.test.js +131 -0
- package/lib/components/paginator/index.js +1 -0
- package/lib/components/paginator/styles/ark.css.js +196 -0
- package/lib/components/paginator/styles/index.js +2 -0
- package/lib/components/spinner/README.md +41 -0
- package/lib/components/spinner/components/spinner.js +105 -0
- package/lib/components/spinner/components/spinner.test.js +50 -0
- package/lib/components/spinner/index.js +1 -0
- package/lib/components/spinner/styles/ark.css.js +658 -0
- package/lib/components/spinner/styles/index.js +2 -0
- package/lib/components/splitview/README.md +63 -0
- package/lib/components/splitview/components/splitview.detail.js +46 -0
- package/lib/components/splitview/components/splitview.detail.test.js +92 -0
- package/lib/components/splitview/components/splitview.js +69 -0
- package/lib/components/splitview/components/splitview.master.js +26 -0
- package/lib/components/splitview/components/splitview.master.test.js +55 -0
- package/lib/components/splitview/components/splitview.test.js +76 -0
- package/lib/components/splitview/index.js +3 -0
- package/lib/components/translate/README.md +56 -0
- package/lib/components/translate/components/translate.js +100 -0
- package/lib/components/translate/components/translate.test.js +226 -0
- package/lib/components/translate/index.js +1 -0
- package/lib/index.js +2 -0
- package/package.json +68 -0
- package/showcase/design/.htaccess +8 -0
- package/showcase/design/core/factories/development/development.factory.js +5 -0
- package/showcase/design/core/factories/development/index.js +1 -0
- package/showcase/design/core/factories/index.js +11 -0
- package/showcase/design/core/factories/standard.factory.js +19 -0
- package/showcase/design/index.html +22 -0
- package/showcase/design/index.js +7 -0
- package/showcase/design/screens/base/audio/audioDemo.js +32 -0
- package/showcase/design/screens/base/audio/index.js +25 -0
- package/showcase/design/screens/base/camera/cameraDemo.js +83 -0
- package/showcase/design/screens/base/camera/index.js +25 -0
- package/showcase/design/screens/base/droparea/dropareaDemo.js +88 -0
- package/showcase/design/screens/base/droparea/index.js +25 -0
- package/showcase/design/screens/base/index.js +42 -0
- package/showcase/design/screens/base/list/index.js +25 -0
- package/showcase/design/screens/base/list/listDemo.js +89 -0
- package/showcase/design/screens/base/paginator/index.js +25 -0
- package/showcase/design/screens/base/paginator/paginatorDemo.js +82 -0
- package/showcase/design/screens/base/root.component.js +294 -0
- package/showcase/design/screens/base/root.routes.js +28 -0
- package/showcase/design/screens/base/spinner/index.js +25 -0
- package/showcase/design/screens/base/spinner/spinnerDemo.js +55 -0
- package/showcase/design/screens/base/splitview/detailDemo.js +40 -0
- package/showcase/design/screens/base/splitview/index.js +25 -0
- package/showcase/design/screens/base/splitview/splitViewDemo.js +58 -0
- package/showcase/design/screens/base/translate/index.js +20 -0
- package/showcase/design/screens/base/translate/translateDemo.js +43 -0
- package/showcase/design/screens/main.js +12 -0
- package/showcase/design/screens/screens.routes.js +23 -0
- package/showcase/index.html +86 -0
- package/showcase/index.js +5 -0
- package/showcase/locales/en/default.json +5 -0
- package/showcase/locales/es/default.json +5 -0
- package/showcase/locales/fr/default.json +5 -0
- package/tsconfig.json +23 -0
- package/webpack.config.cjs +118 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const css = String.raw; export default css`
|
|
2
|
+
.ark-camera {
|
|
3
|
+
display: grid;
|
|
4
|
+
width: fit-content;
|
|
5
|
+
height: fit-content;
|
|
6
|
+
overflow: hidden;
|
|
7
|
+
user-select: none;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.ark-camera__canvas {
|
|
11
|
+
display: none;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.ark-camera__video {
|
|
15
|
+
transform: rotateY(180deg);
|
|
16
|
+
}
|
|
17
|
+
`
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Component } from '../../../base/component/index.js'
|
|
2
|
+
|
|
3
|
+
const tag = 'ark-capture'
|
|
4
|
+
export class Capture extends Component {
|
|
5
|
+
constructor () {
|
|
6
|
+
super()
|
|
7
|
+
const type = this['receive'] || 'emit'
|
|
8
|
+
this.addEventListener(type, this.handle.bind(this))
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
reflectedProperties () {
|
|
12
|
+
return ['receive']
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** @param {Object} context */
|
|
16
|
+
init (context = {}) {
|
|
17
|
+
const data = JSON.parse(this._pop(':scope > data')?.textContent || null)
|
|
18
|
+
this.source = /** @type {object} */ (
|
|
19
|
+
context.source) || data || this.source || {}
|
|
20
|
+
this.template = context.template || this.template || (
|
|
21
|
+
(data) => `${JSON.stringify(data)}`)
|
|
22
|
+
|
|
23
|
+
return super.init()
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
render () {
|
|
27
|
+
const outputTemplate = this._pop(':scope > template')?.innerHTML
|
|
28
|
+
this.template = (
|
|
29
|
+
outputTemplate ? this._format(outputTemplate) : this.template)
|
|
30
|
+
|
|
31
|
+
const output = this.querySelector(':scope > output')
|
|
32
|
+
if (this.template && output) {
|
|
33
|
+
output.innerHTML = this.template(this.source)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return super.render()
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
handle (event) {
|
|
40
|
+
const source = event.detail
|
|
41
|
+
this.init({ source }).render()
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
_format (template) {
|
|
45
|
+
return (data) => Function(`return \`${template}\``).call(data)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
_pop (selector) {
|
|
49
|
+
const element = this.querySelector(selector)
|
|
50
|
+
element?.remove()
|
|
51
|
+
return element
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
Component.define(tag, Capture)
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import './capture.js'
|
|
2
|
+
|
|
3
|
+
describe('Capture', () => {
|
|
4
|
+
let container = null
|
|
5
|
+
|
|
6
|
+
beforeEach(() => {
|
|
7
|
+
container = document.createElement('div')
|
|
8
|
+
document.body.appendChild(container)
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
afterEach(() => {
|
|
12
|
+
container.remove()
|
|
13
|
+
container = null
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
it('can be instantiated', () => {
|
|
17
|
+
container.innerHTML = `
|
|
18
|
+
<ark-capture></ark-capture>
|
|
19
|
+
`
|
|
20
|
+
|
|
21
|
+
const capture = container.querySelector('ark-capture')
|
|
22
|
+
expect(capture).toEqual(capture.init())
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
it('renders the given data detail', () => {
|
|
26
|
+
container.innerHTML = `
|
|
27
|
+
<ark-capture>
|
|
28
|
+
<data>
|
|
29
|
+
{
|
|
30
|
+
"name": "John Doe",
|
|
31
|
+
"job": "Programmer"
|
|
32
|
+
}
|
|
33
|
+
</data>
|
|
34
|
+
<output></output>
|
|
35
|
+
</ark-capture>
|
|
36
|
+
`
|
|
37
|
+
|
|
38
|
+
const capture = container.querySelector('ark-capture')
|
|
39
|
+
const output = capture.querySelector('output')
|
|
40
|
+
|
|
41
|
+
expect(capture.children.length).toEqual(1)
|
|
42
|
+
expect(output.innerHTML).toContain('John Doe')
|
|
43
|
+
expect(output.innerHTML).toContain('Programmer')
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it('renders json data on the given template on the given output', () => {
|
|
47
|
+
container.innerHTML = `
|
|
48
|
+
<ark-capture>
|
|
49
|
+
<data>
|
|
50
|
+
{
|
|
51
|
+
"name": "John Doe",
|
|
52
|
+
"job": "Programmer"
|
|
53
|
+
}
|
|
54
|
+
</data>
|
|
55
|
+
<template>
|
|
56
|
+
<div id="output">
|
|
57
|
+
<strong>\${this.name}</strong>
|
|
58
|
+
<strong>\${this.job}</strong>
|
|
59
|
+
</div>
|
|
60
|
+
</template>
|
|
61
|
+
<output></output>
|
|
62
|
+
</ark-capture>
|
|
63
|
+
`
|
|
64
|
+
|
|
65
|
+
const capture = container.querySelector('ark-capture')
|
|
66
|
+
const output = capture.querySelector('output')
|
|
67
|
+
|
|
68
|
+
expect(capture.children.length).toEqual(1)
|
|
69
|
+
expect(output.children[0].innerHTML).toContain('John Doe')
|
|
70
|
+
expect(output.children[0].innerHTML).toContain('Programmer')
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
it('captures specific custom events and renders its details', () => {
|
|
74
|
+
container.innerHTML = `
|
|
75
|
+
<ark-capture receive="custom">
|
|
76
|
+
<template>
|
|
77
|
+
<div id="output">
|
|
78
|
+
<strong>\${this.name}</strong>
|
|
79
|
+
<strong>\${this.job}</strong>
|
|
80
|
+
</div>
|
|
81
|
+
</template>
|
|
82
|
+
<output></output>
|
|
83
|
+
<p>Adjoint Element</p>
|
|
84
|
+
</ark-capture>
|
|
85
|
+
`
|
|
86
|
+
|
|
87
|
+
const capture = container.querySelector('ark-capture')
|
|
88
|
+
const output = capture.querySelector('output')
|
|
89
|
+
|
|
90
|
+
capture.dispatchEvent(new CustomEvent('custom', {
|
|
91
|
+
bubbles: true,
|
|
92
|
+
detail: {
|
|
93
|
+
name: 'Richard Roe', job: 'Analyst'
|
|
94
|
+
}
|
|
95
|
+
}))
|
|
96
|
+
|
|
97
|
+
expect(capture.children.length).toEqual(2)
|
|
98
|
+
expect(output.children[0].innerHTML).toContain('Richard Roe')
|
|
99
|
+
expect(output.children[0].innerHTML).toContain('Analyst')
|
|
100
|
+
|
|
101
|
+
capture.dispatchEvent(new CustomEvent('custom', {
|
|
102
|
+
bubbles: true,
|
|
103
|
+
detail: {
|
|
104
|
+
name: 'Megan More', job: 'Manager'
|
|
105
|
+
}
|
|
106
|
+
}))
|
|
107
|
+
|
|
108
|
+
expect(capture.children.length).toEqual(2)
|
|
109
|
+
expect(output.children[0].innerHTML).toContain('Megan More')
|
|
110
|
+
expect(output.children[0].innerHTML).toContain('Manager')
|
|
111
|
+
})
|
|
112
|
+
})
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Capture } from './components/capture.js'
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
DROPAREA
|
|
2
|
+
--------
|
|
3
|
+
|
|
4
|
+
The ``ark-droparea`` is an easy to use drag & drop component, that allows the user to upload multiple or single files,
|
|
5
|
+
defining the limit of size and the specific extension of the files.
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
Examples
|
|
9
|
+
========
|
|
10
|
+
|
|
11
|
+
**Default droparea, multiple files**
|
|
12
|
+
|
|
13
|
+
``` html
|
|
14
|
+
|
|
15
|
+
<ark-droparea></ark-droparea>
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
**Single file droparea, use the single atribute**
|
|
19
|
+
|
|
20
|
+
``` html
|
|
21
|
+
|
|
22
|
+
<ark-droparea single></ark-droparea>
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
**With the attributes accept and max-size specified in megabytes**
|
|
26
|
+
|
|
27
|
+
``` html
|
|
28
|
+
<ark-droparea accept="jpg, png, gif" max-size="10" ></ark-droparea>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
Attributes
|
|
34
|
+
----------
|
|
35
|
+
|
|
36
|
+
| Name | Type | Default | Description |
|
|
37
|
+
| :------: | :-----: | :--------: | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------: |
|
|
38
|
+
| single | boolean | (multiple) | Limits the number of files to a single one, if not set, recieves multiple files by default |
|
|
39
|
+
| max-size | string | (no limit) | Specifies the size of the file in megabytes |
|
|
40
|
+
| accept | string | (any) | Indicates the type of files, with values separated by commas: (png, jpeg, gif, etc.). It's also possible to specify general file types: (image, video, text, audio) |
|
|
41
|
+
|
|
42
|
+
Properties
|
|
43
|
+
----------
|
|
44
|
+
|
|
45
|
+
| Name | Type | Default | Description |
|
|
46
|
+
| :-------: | :-----: | :--------: | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------: |
|
|
47
|
+
| single | boolean | (multiple) | Limits the number of files to a single one, if not set, recieves multiple files by default |
|
|
48
|
+
| maxSize | string | (no limit) | Specifies the size of the file in bytes |
|
|
49
|
+
| accept | string | (any) | Indicates the type of files, with values separated by commas: (png, jpeg, gif, etc.), It's also possible to specify general file types: (image, video, text, audio) |
|
|
50
|
+
| fileList | string | - | Returns the list of files droped |
|
|
51
|
+
| mediaList | string | - | Returns a list of media objects |
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { Component } from '../../../base/component/index.js'
|
|
2
|
+
import './droparea.js'
|
|
3
|
+
// @ts-ignore
|
|
4
|
+
const tag = 'ark-droparea-preview'
|
|
5
|
+
|
|
6
|
+
export class DropareaPreview extends Component {
|
|
7
|
+
init(_context = {}) {
|
|
8
|
+
return super.init()
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
render() {
|
|
12
|
+
this.content = /* html */ `
|
|
13
|
+
<ul data-preview-list class="ark-droparea-preview__list drag-sort-enable"></ul>
|
|
14
|
+
`
|
|
15
|
+
return super.render()
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
previewFile(file) {
|
|
19
|
+
const blobUrl = URL.createObjectURL(file)
|
|
20
|
+
const fileType = file.type.split('/')[0]
|
|
21
|
+
const previewZone = this.select('[data-preview-list]')
|
|
22
|
+
const picture = document.createElement('li')
|
|
23
|
+
picture.className = 'ark-droparea-preview__frame'
|
|
24
|
+
const removeButton = document.createElement('button')
|
|
25
|
+
removeButton.innerText = '⨯'
|
|
26
|
+
removeButton.className = 'ark-droparea__remove'
|
|
27
|
+
|
|
28
|
+
if (fileType != 'image') {
|
|
29
|
+
picture.innerHTML = `<p>${file.name}</p>`
|
|
30
|
+
picture.setAttribute('data', `${blobUrl}`)
|
|
31
|
+
} else {
|
|
32
|
+
picture.style.backgroundImage = `url('${blobUrl}')`
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
removeButton.addEventListener('click', this.removeFile.bind(this, file))
|
|
36
|
+
picture.appendChild(removeButton)
|
|
37
|
+
previewZone.appendChild(picture)
|
|
38
|
+
this.toggleVisibility()
|
|
39
|
+
|
|
40
|
+
if (!this.droparea.hasAttribute('single')) {
|
|
41
|
+
picture.setAttribute('index', this.fileIndex(file))
|
|
42
|
+
this.enableDragSort('drag-sort-enable')
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
toggleVisibility() {
|
|
47
|
+
const previewZone = this.select('[data-preview-list]')
|
|
48
|
+
this.files.length !== 0
|
|
49
|
+
? (previewZone.style.display = 'grid')
|
|
50
|
+
: (previewZone.style.display = 'none')
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/* DragSort Functionality */
|
|
54
|
+
|
|
55
|
+
enableDragSort(listClass) {
|
|
56
|
+
const sortableLists = this.getElementsByClassName(listClass)
|
|
57
|
+
Array.prototype.map.call(sortableLists, (list) => {
|
|
58
|
+
this.enableDragList(list)
|
|
59
|
+
})
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
enableDragList(list) {
|
|
63
|
+
Array.prototype.map.call(list.children, (item) => {
|
|
64
|
+
this.enableDragItem(item)
|
|
65
|
+
})
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
enableDragItem(item) {
|
|
69
|
+
item.setAttribute('draggable', true)
|
|
70
|
+
item.addEventListener('drag', this.handleDrag.bind(this, item))
|
|
71
|
+
item.addEventListener('dragend', this.handleDrop, false)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/* istanbul ignore next */
|
|
75
|
+
handleDrag(item, event) {
|
|
76
|
+
const selectedItem = item,
|
|
77
|
+
list = selectedItem.parentNode,
|
|
78
|
+
x = event.clientX,
|
|
79
|
+
y = event.clientY
|
|
80
|
+
|
|
81
|
+
selectedItem.classList.add('drag-sort-active')
|
|
82
|
+
let swapItem =
|
|
83
|
+
document.elementFromPoint(x, y) === null
|
|
84
|
+
? selectedItem
|
|
85
|
+
: document.elementFromPoint(x, y)
|
|
86
|
+
if (list === swapItem.parentNode) {
|
|
87
|
+
swapItem = (
|
|
88
|
+
swapItem !== selectedItem.nextSibling ?
|
|
89
|
+
swapItem : swapItem.nextSibling)
|
|
90
|
+
list.insertBefore(selectedItem, swapItem)
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
handleDrop(item) {
|
|
95
|
+
const droparea = item.target.closest('ark-droparea')
|
|
96
|
+
droparea.preview.createNewFileList()
|
|
97
|
+
item.target.classList.remove('drag-sort-active')
|
|
98
|
+
droparea.preview.dispatchAlterEvent()
|
|
99
|
+
}
|
|
100
|
+
/*----------------------------------------------------*/
|
|
101
|
+
|
|
102
|
+
dispatchAlterEvent() {
|
|
103
|
+
this.emit('alter', this.mediaList)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
createNewFileList() {
|
|
107
|
+
const nodeList = this.querySelectorAll('li')
|
|
108
|
+
const newList = []
|
|
109
|
+
nodeList.forEach((item, index) => {
|
|
110
|
+
newList.push(this.droparea.fileList[item.getAttribute('index')])
|
|
111
|
+
item.setAttribute('index', index)
|
|
112
|
+
})
|
|
113
|
+
this.droparea.fileList = newList
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
fileExists(file) {
|
|
117
|
+
const present = this.files.some((item) => item.name === file.name)
|
|
118
|
+
return present
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
removeFile(file, event) {
|
|
122
|
+
let element = event.target
|
|
123
|
+
const fileIndex = this.droparea.fileList.indexOf(file)
|
|
124
|
+
this.droparea.fileList.splice(fileIndex, 1)
|
|
125
|
+
element.parentNode.remove()
|
|
126
|
+
this.selectAll('li').forEach((item, index) =>
|
|
127
|
+
item.setAttribute('index', index)
|
|
128
|
+
)
|
|
129
|
+
this.toggleVisibility()
|
|
130
|
+
this.dispatchAlterEvent()
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
fileIndex(file) {
|
|
134
|
+
return this.droparea.fileList.indexOf(file)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
get droparea() {
|
|
138
|
+
return this.closest('.ark-droparea')
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
get mediaList() {
|
|
142
|
+
const mediaList = []
|
|
143
|
+
this.droparea.fileList.map((file) => {
|
|
144
|
+
mediaList.push({
|
|
145
|
+
name: file.name,
|
|
146
|
+
type: file.type,
|
|
147
|
+
size: file.size,
|
|
148
|
+
url: URL.createObjectURL(file),
|
|
149
|
+
})
|
|
150
|
+
})
|
|
151
|
+
return mediaList
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
get files() {
|
|
155
|
+
return this.droparea.fileList
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
Component.define(tag, DropareaPreview)
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { jest } from '@jest/globals'
|
|
2
|
+
import './droparea-preview.js'
|
|
3
|
+
|
|
4
|
+
describe('Droparea', () => {
|
|
5
|
+
const createBubbledEvent = (type, props = {}) => {
|
|
6
|
+
const event = new Event(type, {
|
|
7
|
+
bubbles: true,
|
|
8
|
+
})
|
|
9
|
+
Object.assign(event, props)
|
|
10
|
+
return event
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
global.URL.createObjectURL = jest.fn()
|
|
14
|
+
global.document.elementFromPoint = jest.fn()
|
|
15
|
+
|
|
16
|
+
let container = null
|
|
17
|
+
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
container = document.createElement('div')
|
|
20
|
+
document.body.appendChild(container)
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
afterEach(() => {
|
|
24
|
+
container.remove()
|
|
25
|
+
container = null
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it('can be instantiated', () => {
|
|
29
|
+
container.innerHTML = /* html */ `
|
|
30
|
+
<ark-droparea></ark-droparea>
|
|
31
|
+
`
|
|
32
|
+
const droparea = container.querySelector('ark-droparea')
|
|
33
|
+
const preview = droparea.querySelector('ark-droparea-preview')
|
|
34
|
+
expect(preview).toBe(preview.init())
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it('Item can be removed', () => {
|
|
38
|
+
container.innerHTML = /* html */ `
|
|
39
|
+
<ark-droparea></ark-droparea>
|
|
40
|
+
`
|
|
41
|
+
|
|
42
|
+
const droparea = container.querySelector('ark-droparea')
|
|
43
|
+
const preview = droparea.querySelector('[data-preview-list]')
|
|
44
|
+
const dropZone = droparea.querySelector('.ark-droparea__form')
|
|
45
|
+
const myFile = new File(['image'], 'Doggy.png', {
|
|
46
|
+
type: 'image/png',
|
|
47
|
+
})
|
|
48
|
+
const myFile2 = new File(['image'], 'Scooby.png', {
|
|
49
|
+
type: 'image/png',
|
|
50
|
+
})
|
|
51
|
+
const dropEvent = createBubbledEvent('drop', {
|
|
52
|
+
clientX: 0,
|
|
53
|
+
clientY: 1,
|
|
54
|
+
dataTransfer: {
|
|
55
|
+
files: [myFile, myFile2],
|
|
56
|
+
},
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
dropZone.dispatchEvent(dropEvent)
|
|
60
|
+
preview.querySelector('button').click()
|
|
61
|
+
preview.querySelector('button').click()
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
it('Can drag previews and sort a new list', () => {
|
|
65
|
+
container.innerHTML = /* html */ `
|
|
66
|
+
<ark-droparea></ark-droparea>
|
|
67
|
+
`
|
|
68
|
+
|
|
69
|
+
const droparea = container.querySelector('ark-droparea')
|
|
70
|
+
const preview = droparea.querySelector('[data-preview-list]')
|
|
71
|
+
const dropZone = droparea.querySelector('.ark-droparea__form')
|
|
72
|
+
const myFile = new File(['image'], 'Doggy.png', {
|
|
73
|
+
type: 'image/png',
|
|
74
|
+
})
|
|
75
|
+
const myFile2 = new File(['image'], 'Scooby.png', {
|
|
76
|
+
type: 'image/png',
|
|
77
|
+
})
|
|
78
|
+
const dropEvent = createBubbledEvent('drop', {
|
|
79
|
+
clientX: 0,
|
|
80
|
+
clientY: 1,
|
|
81
|
+
dataTransfer: {
|
|
82
|
+
files: [myFile, myFile2],
|
|
83
|
+
},
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
dropZone.dispatchEvent(dropEvent)
|
|
87
|
+
|
|
88
|
+
preview.handleDrag = jest.fn()
|
|
89
|
+
|
|
90
|
+
const getThumbnails = () =>
|
|
91
|
+
Array.from(preview.querySelectorAll('.ark-droparea-preview__frame'))
|
|
92
|
+
|
|
93
|
+
const thumbnails = getThumbnails()
|
|
94
|
+
|
|
95
|
+
const startingNode = thumbnails[0]
|
|
96
|
+
const endingNode = thumbnails[1]
|
|
97
|
+
|
|
98
|
+
startingNode.dispatchEvent(
|
|
99
|
+
createBubbledEvent('dragstart', { clientX: 0, clientY: 0 })
|
|
100
|
+
)
|
|
101
|
+
endingNode.dispatchEvent(
|
|
102
|
+
createBubbledEvent('dragend', { clientX: 0, clientY: 1 })
|
|
103
|
+
)
|
|
104
|
+
})
|
|
105
|
+
})
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { Component } from '../../../base/component/index.js'
|
|
2
|
+
import './droparea-preview.js'
|
|
3
|
+
import styles from '../styles/index.js'
|
|
4
|
+
// @ts-ignore
|
|
5
|
+
const tag = 'ark-droparea'
|
|
6
|
+
|
|
7
|
+
export class Droparea extends Component {
|
|
8
|
+
init(context = {}) {
|
|
9
|
+
this.fileList = []
|
|
10
|
+
this.contextFiles = context.contextFiles || this.contextFiles || []
|
|
11
|
+
this.accept = context.accept || this.accept
|
|
12
|
+
this.single = this.hasAttribute('single')
|
|
13
|
+
this.maxSize = context.maxSize || this.maxSize || ''
|
|
14
|
+
return super.init()
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
render() {
|
|
18
|
+
this.content = /* html */ `
|
|
19
|
+
<form class="${tag}__form">
|
|
20
|
+
<div class="${tag}__header">
|
|
21
|
+
<div class="${tag}__open">${this.title || 'Upload'}</div>
|
|
22
|
+
<input id="fileElement"class="${tag}__input" type="file"
|
|
23
|
+
data-input multiple />
|
|
24
|
+
</div>
|
|
25
|
+
<ark-droparea-preview></ark-droparea-preview>
|
|
26
|
+
</form>
|
|
27
|
+
`
|
|
28
|
+
this.dragDropEvents = ['dragenter', 'dragover', 'dragleave', 'drop']
|
|
29
|
+
this.dragEvents = this.dragDropEvents.slice(0, 2)
|
|
30
|
+
this.dropEvents = this.dragDropEvents.slice(2)
|
|
31
|
+
this._input = this.select('.ark-droparea__input')
|
|
32
|
+
this.openButton = this.select('.ark-droparea__open')
|
|
33
|
+
|
|
34
|
+
return super.render()
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
reflectedProperties() {
|
|
38
|
+
return ['size', 'accept', 'maxSize', 'title']
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async load() {
|
|
42
|
+
this.dragDropEvents.forEach((eventName) => {
|
|
43
|
+
this.addEventListener(eventName, this.preventDefaults, false)
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
this.dragEvents.forEach((eventName) => {
|
|
47
|
+
this.addEventListener(eventName, this.highlight, false)
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
this.dropEvents.forEach((eventName) => {
|
|
51
|
+
this.addEventListener(eventName, this.unhighlight, false)
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
this.addEventListener('drop', this.handleDrop, false)
|
|
55
|
+
this._input.addEventListener('change', this.onChange.bind(this))
|
|
56
|
+
this.openButton.addEventListener('click', this.openInput.bind(this))
|
|
57
|
+
|
|
58
|
+
/* istanbul ignore else */
|
|
59
|
+
if (this.contextFiles) {
|
|
60
|
+
await this.handleFiles(this.contextFiles)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
openInput(event) {
|
|
65
|
+
event.stopPropagation()
|
|
66
|
+
const input = this.select('[data-input]')
|
|
67
|
+
input.click()
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
preventDefaults(event) {
|
|
71
|
+
event.preventDefault()
|
|
72
|
+
event.stopPropagation()
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
highlight(event) {
|
|
76
|
+
this.dropZone.classList.add('highlight')
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
unhighlight(event) {
|
|
80
|
+
this.dropZone.classList.remove('highlight')
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
handleDrop(event) {
|
|
84
|
+
event.stopPropagation()
|
|
85
|
+
let data = event.dataTransfer
|
|
86
|
+
let files = data.files
|
|
87
|
+
this.handleFiles(files)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
onChange(event) {
|
|
91
|
+
event.stopPropagation()
|
|
92
|
+
const input = event.target
|
|
93
|
+
const files = input.files
|
|
94
|
+
this.handleFiles(files)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
handleFiles(files) {
|
|
98
|
+
if (this.single) {
|
|
99
|
+
files = [files[0]]
|
|
100
|
+
if (
|
|
101
|
+
files[0] != undefined &&
|
|
102
|
+
this.validate(files) &&
|
|
103
|
+
!this.preview.fileExists(files[0]) &&
|
|
104
|
+
this.maxSizeValidate(files[0])
|
|
105
|
+
) {
|
|
106
|
+
this.fileList[0] = files[0]
|
|
107
|
+
this.preview.querySelector('[data-preview-list]').innerHTML = ''
|
|
108
|
+
this.preview.previewFile(files[0])
|
|
109
|
+
}
|
|
110
|
+
} else {
|
|
111
|
+
files = [...files]
|
|
112
|
+
if (!files.includes(undefined) && this.validate(files)) {
|
|
113
|
+
files.forEach((file) => {
|
|
114
|
+
/*istanbul ignore else*/
|
|
115
|
+
if (!this.preview.fileExists(file) && this.maxSizeValidate(file)) {
|
|
116
|
+
this.fileList.push(file)
|
|
117
|
+
this.preview.previewFile(file)
|
|
118
|
+
}
|
|
119
|
+
})
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
this.preview.dispatchAlterEvent()
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
validate(fileList) {
|
|
126
|
+
if (!this.accept || this.accept.length === 0) return true
|
|
127
|
+
const acceptList = this.accept.split(',').map(
|
|
128
|
+
(s) => s.trim().toLowerCase())
|
|
129
|
+
const hasAudio = acceptList.indexOf('audio') >= 0
|
|
130
|
+
const hasVideo = acceptList.indexOf('video') >= 0
|
|
131
|
+
const hasImage = acceptList.indexOf('image') >= 0
|
|
132
|
+
const hasText = acceptList.indexOf('text') >= 0
|
|
133
|
+
|
|
134
|
+
for (let i = 0, len = fileList.length; i < len; ++i) {
|
|
135
|
+
let ext = '' + fileList[i].name.split('.').pop().toLowerCase()
|
|
136
|
+
if (acceptList.indexOf(ext) >= 0) continue
|
|
137
|
+
if (hasAudio && fileList[i].type.split('/')[0] === 'audio') continue
|
|
138
|
+
if (hasVideo && fileList[i].type.split('/')[0] === 'video') continue
|
|
139
|
+
if (hasImage && fileList[i].type.split('/')[0] === 'image') continue
|
|
140
|
+
if (hasText && fileList[i].type.split('/')[0] === 'text') continue
|
|
141
|
+
|
|
142
|
+
return false
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return true
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
maxSizeValidate (file) {
|
|
149
|
+
return true
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
get dropZone() {
|
|
153
|
+
return this.select('.ark-droparea__form')
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
get preview() {
|
|
157
|
+
return this.select('ark-droparea-preview')
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
get mediaList() {
|
|
161
|
+
return this.preview.mediaList
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
Component.define(tag, Droparea, styles)
|