@knowark/componarkjs 1.13.4 → 1.14.1
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/README.md +57 -45
- package/lib/base/component/component.js +142 -20
- package/lib/base/component/component.test.js +753 -374
- package/lib/base/component/index.js +3 -0
- package/lib/base/styles/index.js +4 -1
- package/lib/base/utils/define.js +30 -7
- package/lib/base/utils/define.test.js +129 -42
- package/lib/base/utils/format.js +12 -6
- package/lib/base/utils/format.test.js +16 -16
- package/lib/base/utils/helpers.js +42 -9
- package/lib/base/utils/helpers.test.js +134 -115
- package/lib/base/utils/index.js +1 -0
- package/lib/base/utils/slots.js +3 -2
- package/lib/base/utils/slots.test.js +38 -38
- package/lib/base/utils/uuid.js +1 -1
- package/lib/base/utils/uuid.test.js +13 -13
- package/lib/components/audio/components/audio.js +36 -3
- package/lib/components/audio/components/audio.test.js +120 -90
- package/lib/components/audio/index.js +1 -0
- package/lib/components/audio/styles/index.js +5 -1
- package/lib/components/camera/components/camera.js +15 -0
- package/lib/components/camera/components/camera.test.js +96 -91
- package/lib/components/camera/index.js +1 -0
- package/lib/components/camera/styles/index.js +5 -1
- package/lib/components/capture/components/capture.js +48 -4
- package/lib/components/capture/components/capture.test.js +165 -97
- package/lib/components/capture/index.js +1 -0
- package/lib/components/droparea/components/droparea-preview.js +114 -19
- package/lib/components/droparea/components/droparea-preview.test.js +344 -80
- package/lib/components/droparea/components/droparea.js +82 -6
- package/lib/components/droparea/components/droparea.test.js +309 -299
- package/lib/components/droparea/index.js +1 -0
- package/lib/components/droparea/styles/index.js +5 -1
- package/lib/components/emit/components/emit.js +34 -4
- package/lib/components/emit/components/emit.test.js +192 -134
- package/lib/components/emit/index.js +1 -0
- package/lib/components/index.js +2 -1
- package/lib/components/list/components/item.js +6 -0
- package/lib/components/list/components/item.test.js +69 -68
- package/lib/components/list/components/list.js +51 -7
- package/lib/components/list/components/list.test.js +358 -227
- package/lib/components/list/index.js +1 -0
- package/lib/components/paginator/components/paginator.js +34 -8
- package/lib/components/paginator/components/paginator.test.js +146 -143
- package/lib/components/paginator/index.js +1 -0
- package/lib/components/paginator/styles/index.js +5 -1
- package/lib/components/spinner/components/spinner.js +10 -0
- package/lib/components/spinner/components/spinner.test.js +36 -41
- package/lib/components/spinner/index.js +1 -0
- package/lib/components/spinner/styles/index.js +5 -1
- package/lib/components/splitview/components/splitview.detail.js +10 -1
- package/lib/components/splitview/components/splitview.detail.test.js +75 -73
- package/lib/components/splitview/components/splitview.js +54 -11
- package/lib/components/splitview/components/splitview.master.js +37 -2
- package/lib/components/splitview/components/splitview.master.test.js +52 -52
- package/lib/components/splitview/components/splitview.test.js +135 -31
- package/lib/components/splitview/index.js +1 -0
- package/lib/components/translate/components/translate.js +65 -14
- package/lib/components/translate/components/translate.test.js +658 -131
- package/lib/components/translate/index.js +1 -0
- package/lib/index.js +3 -0
- package/package.json +4 -27
- package/scripts/node-test-setup.js +94 -0
- package/tsconfig.json +1 -1
- package/types/base/component/component.d.ts +43 -8
- package/types/base/component/component.d.ts.map +1 -1
- package/types/base/component/index.d.ts +4 -6
- package/types/base/component/index.d.ts.map +1 -1
- package/types/base/styles/index.d.ts +3 -2
- package/types/base/styles/index.d.ts.map +1 -1
- package/types/base/utils/define.d.ts +3 -2
- package/types/base/utils/define.d.ts.map +1 -1
- package/types/base/utils/format.d.ts +12 -6
- package/types/base/utils/format.d.ts.map +1 -1
- package/types/base/utils/helpers.d.ts +27 -7
- package/types/base/utils/helpers.d.ts.map +1 -1
- package/types/base/utils/slots.d.ts +8 -10
- package/types/base/utils/slots.d.ts.map +1 -1
- package/types/base/utils/uuid.d.ts +1 -1
- package/types/base/utils/uuid.d.ts.map +1 -1
- package/types/components/audio/components/audio.d.ts +23 -9
- package/types/components/audio/components/audio.d.ts.map +1 -1
- package/types/components/audio/styles/index.d.ts +3 -2
- package/types/components/audio/styles/index.d.ts.map +1 -1
- package/types/components/camera/components/camera.d.ts +11 -3
- package/types/components/camera/components/camera.d.ts.map +1 -1
- package/types/components/camera/styles/index.d.ts +3 -2
- package/types/components/camera/styles/index.d.ts.map +1 -1
- package/types/components/capture/components/capture.d.ts +23 -3
- package/types/components/capture/components/capture.d.ts.map +1 -1
- package/types/components/droparea/components/droparea-preview.d.ts +64 -11
- package/types/components/droparea/components/droparea-preview.d.ts.map +1 -1
- package/types/components/droparea/components/droparea.d.ts +58 -13
- package/types/components/droparea/components/droparea.d.ts.map +1 -1
- package/types/components/droparea/styles/index.d.ts +3 -2
- package/types/components/droparea/styles/index.d.ts.map +1 -1
- package/types/components/emit/components/emit.d.ts +15 -3
- package/types/components/emit/components/emit.d.ts.map +1 -1
- package/types/components/list/components/item.d.ts +8 -1
- package/types/components/list/components/item.d.ts.map +1 -1
- package/types/components/list/components/list.d.ts +23 -5
- package/types/components/list/components/list.d.ts.map +1 -1
- package/types/components/paginator/components/paginator.d.ts +32 -8
- package/types/components/paginator/components/paginator.d.ts.map +1 -1
- package/types/components/paginator/styles/index.d.ts +3 -2
- package/types/components/paginator/styles/index.d.ts.map +1 -1
- package/types/components/spinner/components/spinner.d.ts +14 -3
- package/types/components/spinner/components/spinner.d.ts.map +1 -1
- package/types/components/spinner/styles/index.d.ts +3 -2
- package/types/components/spinner/styles/index.d.ts.map +1 -1
- package/types/components/splitview/components/splitview.d.ts +22 -4
- package/types/components/splitview/components/splitview.d.ts.map +1 -1
- package/types/components/splitview/components/splitview.detail.d.ts +12 -2
- package/types/components/splitview/components/splitview.detail.d.ts.map +1 -1
- package/types/components/splitview/components/splitview.master.d.ts +12 -1
- package/types/components/splitview/components/splitview.master.d.ts.map +1 -1
- package/types/components/translate/components/translate.d.ts +44 -10
- package/types/components/translate/components/translate.d.ts.map +1 -1
|
@@ -1,107 +1,371 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { it, mock } from 'node:test'
|
|
2
|
+
import assert from 'node:assert/strict'
|
|
2
3
|
import './droparea-preview.js'
|
|
3
4
|
|
|
4
|
-
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
}
|
|
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
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
let objectURLCount = 0
|
|
14
|
+
global.URL.createObjectURL = (
|
|
15
|
+
/** @type {(obj: Blob | MediaSource) => string} */ (
|
|
16
|
+
mock.fn(() => `mock://data/url/${objectURLCount++}`)))
|
|
17
|
+
global.URL.revokeObjectURL = (
|
|
18
|
+
/** @type {(url: string) => void} */ (mock.fn()))
|
|
19
|
+
global.document.elementFromPoint = (
|
|
20
|
+
/** @type {(x: number, y: number) => Element} */ (mock.fn()))
|
|
17
21
|
|
|
18
|
-
|
|
22
|
+
let container = null
|
|
19
23
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
+
const setup = () => {
|
|
25
|
+
document.body.innerHTML = ''
|
|
26
|
+
objectURLCount = 0
|
|
27
|
+
global.URL.createObjectURL.mock.resetCalls()
|
|
28
|
+
global.URL.revokeObjectURL.mock.resetCalls()
|
|
29
|
+
container = document.createElement('div')
|
|
30
|
+
document.body.appendChild(container)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
it('can be instantiated', () => {
|
|
34
|
+
setup()
|
|
35
|
+
container.innerHTML = /* html */ `
|
|
36
|
+
<ark-droparea></ark-droparea>
|
|
37
|
+
`
|
|
38
|
+
const droparea = container.querySelector('ark-droparea')
|
|
39
|
+
const preview = droparea.querySelector('ark-droparea-preview')
|
|
40
|
+
assert.strictEqual(preview, preview.init())
|
|
41
|
+
})
|
|
24
42
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
43
|
+
it('Item can be removed', () => {
|
|
44
|
+
setup()
|
|
45
|
+
container.innerHTML = /* html */ `
|
|
46
|
+
<ark-droparea></ark-droparea>
|
|
47
|
+
`
|
|
48
|
+
|
|
49
|
+
const droparea = container.querySelector('ark-droparea')
|
|
50
|
+
const preview = droparea.querySelector('[data-preview-list]')
|
|
51
|
+
const dropZone = droparea.querySelector('.ark-droparea__form')
|
|
52
|
+
const myFile = new File(['image'], 'Doggy.png', {
|
|
53
|
+
type: 'image/png'
|
|
54
|
+
})
|
|
55
|
+
const myFile2 = new File(['image'], 'Scooby.png', {
|
|
56
|
+
type: 'image/png'
|
|
57
|
+
})
|
|
58
|
+
const dropEvent = createBubbledEvent('drop', {
|
|
59
|
+
clientX: 0,
|
|
60
|
+
clientY: 1,
|
|
61
|
+
dataTransfer: {
|
|
62
|
+
files: [myFile, myFile2]
|
|
63
|
+
}
|
|
28
64
|
})
|
|
29
65
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
66
|
+
dropZone.dispatchEvent(dropEvent)
|
|
67
|
+
preview.querySelector('button').click()
|
|
68
|
+
preview.querySelector('button').click()
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
it('Can drag previews and sort a new list', () => {
|
|
72
|
+
setup()
|
|
73
|
+
container.innerHTML = /* html */ `
|
|
74
|
+
<ark-droparea></ark-droparea>
|
|
75
|
+
`
|
|
76
|
+
|
|
77
|
+
const droparea = container.querySelector('ark-droparea')
|
|
78
|
+
const preview = droparea.querySelector('[data-preview-list]')
|
|
79
|
+
const dropZone = droparea.querySelector('.ark-droparea__form')
|
|
80
|
+
const myFile = new File(['image'], 'Doggy.png', {
|
|
81
|
+
type: 'image/png'
|
|
82
|
+
})
|
|
83
|
+
const myFile2 = new File(['image'], 'Scooby.png', {
|
|
84
|
+
type: 'image/png'
|
|
85
|
+
})
|
|
86
|
+
const dropEvent = createBubbledEvent('drop', {
|
|
87
|
+
clientX: 0,
|
|
88
|
+
clientY: 1,
|
|
89
|
+
dataTransfer: {
|
|
90
|
+
files: [myFile, myFile2]
|
|
91
|
+
}
|
|
37
92
|
})
|
|
38
93
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
94
|
+
dropZone.dispatchEvent(dropEvent)
|
|
95
|
+
|
|
96
|
+
preview.handleDrag = mock.fn()
|
|
97
|
+
|
|
98
|
+
const getThumbnails = () =>
|
|
99
|
+
Array.from(preview.querySelectorAll('.ark-droparea-preview__frame'))
|
|
100
|
+
|
|
101
|
+
const thumbnails = getThumbnails()
|
|
102
|
+
|
|
103
|
+
const startingNode = thumbnails[0]
|
|
104
|
+
const endingNode = thumbnails[1]
|
|
105
|
+
|
|
106
|
+
startingNode.dispatchEvent(
|
|
107
|
+
createBubbledEvent('dragstart', { clientX: 0, clientY: 0 })
|
|
108
|
+
)
|
|
109
|
+
endingNode.dispatchEvent(
|
|
110
|
+
createBubbledEvent('dragend', { clientX: 0, clientY: 1 })
|
|
111
|
+
)
|
|
112
|
+
})
|
|
43
113
|
|
|
114
|
+
it('handleDrag keeps the selected item when elementFromPoint returns null', () => {
|
|
115
|
+
setup()
|
|
116
|
+
const preview = document.createElement('ark-droparea-preview')
|
|
117
|
+
const list = document.createElement('ul')
|
|
118
|
+
const firstItem = document.createElement('li')
|
|
119
|
+
const secondItem = document.createElement('li')
|
|
120
|
+
list.append(firstItem, secondItem)
|
|
121
|
+
preview.appendChild(list)
|
|
122
|
+
document.body.appendChild(preview)
|
|
123
|
+
|
|
124
|
+
const originalElementFromPoint = document.elementFromPoint
|
|
125
|
+
document.elementFromPoint = () => null
|
|
126
|
+
|
|
127
|
+
preview.handleDrag(firstItem, { clientX: 0, clientY: 0 })
|
|
128
|
+
|
|
129
|
+
assert.ok(firstItem.classList.contains('drag-sort-active'))
|
|
130
|
+
assert.strictEqual(list.firstElementChild, firstItem)
|
|
131
|
+
|
|
132
|
+
document.elementFromPoint = originalElementFromPoint
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
it('handleDrag moves the selected item after its next sibling', () => {
|
|
136
|
+
setup()
|
|
137
|
+
const preview = document.createElement('ark-droparea-preview')
|
|
138
|
+
const list = document.createElement('ul')
|
|
139
|
+
const firstItem = document.createElement('li')
|
|
140
|
+
const secondItem = document.createElement('li')
|
|
141
|
+
const thirdItem = document.createElement('li')
|
|
142
|
+
list.append(firstItem, secondItem, thirdItem)
|
|
143
|
+
preview.appendChild(list)
|
|
144
|
+
document.body.appendChild(preview)
|
|
145
|
+
|
|
146
|
+
const originalElementFromPoint = document.elementFromPoint
|
|
147
|
+
document.elementFromPoint = () => secondItem
|
|
148
|
+
|
|
149
|
+
preview.handleDrag(firstItem, { clientX: 1, clientY: 1 })
|
|
150
|
+
|
|
151
|
+
assert.strictEqual(list.children[0], secondItem)
|
|
152
|
+
assert.strictEqual(list.children[1], firstItem)
|
|
153
|
+
assert.strictEqual(list.children[2], thirdItem)
|
|
154
|
+
|
|
155
|
+
document.elementFromPoint = originalElementFromPoint
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
it('does not add duplicate drag listeners to existing previews', () => {
|
|
159
|
+
setup()
|
|
160
|
+
const counts = new WeakMap()
|
|
161
|
+
const originalAddEventListener = Element.prototype.addEventListener
|
|
162
|
+
Element.prototype.addEventListener = function (type, listener, options) {
|
|
163
|
+
const isDragEvent = type === 'drag' || type === 'dragend'
|
|
164
|
+
if (this.tagName === 'LI' && isDragEvent) {
|
|
165
|
+
const itemCounts = counts.get(this) || { drag: 0, dragend: 0 }
|
|
166
|
+
itemCounts[type] += 1
|
|
167
|
+
counts.set(this, itemCounts)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return originalAddEventListener.call(this, type, listener, options)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
try {
|
|
174
|
+
container.innerHTML = /* html */ `
|
|
175
|
+
<ark-droparea></ark-droparea>
|
|
176
|
+
`
|
|
44
177
|
const droparea = container.querySelector('ark-droparea')
|
|
45
|
-
const preview = droparea.querySelector('[data-preview-list]')
|
|
46
178
|
const dropZone = droparea.querySelector('.ark-droparea__form')
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
const myFile2 = new File(['image'], 'Scooby.png', {
|
|
51
|
-
type: 'image/png'
|
|
179
|
+
|
|
180
|
+
const firstBatch = createBubbledEvent('drop', {
|
|
181
|
+
dataTransfer: { files: [new File(['image'], 'Doggy.png', { type: 'image/png' })] }
|
|
52
182
|
})
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
clientY: 1,
|
|
56
|
-
dataTransfer: {
|
|
57
|
-
files: [myFile, myFile2]
|
|
58
|
-
}
|
|
183
|
+
const secondBatch = createBubbledEvent('drop', {
|
|
184
|
+
dataTransfer: { files: [new File(['image'], 'Scooby.png', { type: 'image/png' })] }
|
|
59
185
|
})
|
|
60
186
|
|
|
61
|
-
dropZone.dispatchEvent(
|
|
62
|
-
|
|
63
|
-
|
|
187
|
+
dropZone.dispatchEvent(firstBatch)
|
|
188
|
+
dropZone.dispatchEvent(secondBatch)
|
|
189
|
+
|
|
190
|
+
const firstItem = droparea.querySelector('.ark-droparea-preview__frame')
|
|
191
|
+
const itemCounts = counts.get(firstItem)
|
|
192
|
+
|
|
193
|
+
assert.deepStrictEqual(itemCounts.drag, 1)
|
|
194
|
+
assert.deepStrictEqual(itemCounts.dragend, 1)
|
|
195
|
+
} finally {
|
|
196
|
+
Element.prototype.addEventListener = originalAddEventListener
|
|
197
|
+
}
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
it('reuses object URLs for the media list and revokes on file removal', () => {
|
|
201
|
+
setup()
|
|
202
|
+
container.innerHTML = /* html */ `
|
|
203
|
+
<ark-droparea></ark-droparea>
|
|
204
|
+
`
|
|
205
|
+
const droparea = container.querySelector('ark-droparea')
|
|
206
|
+
const dropZone = droparea.querySelector('.ark-droparea__form')
|
|
207
|
+
const myFile = new File(['image'], 'Doggy.png', {
|
|
208
|
+
type: 'image/png'
|
|
64
209
|
})
|
|
65
210
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
`
|
|
211
|
+
dropZone.dispatchEvent(createBubbledEvent('drop', {
|
|
212
|
+
dataTransfer: { files: [myFile] }
|
|
213
|
+
}))
|
|
70
214
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
const dropEvent = createBubbledEvent('drop', {
|
|
81
|
-
clientX: 0,
|
|
82
|
-
clientY: 1,
|
|
83
|
-
dataTransfer: {
|
|
84
|
-
files: [myFile, myFile2]
|
|
85
|
-
}
|
|
86
|
-
})
|
|
215
|
+
const callsBeforeMediaRead = global.URL.createObjectURL.mock.calls.length
|
|
216
|
+
const mediaUrl = droparea.mediaList[0].url
|
|
217
|
+
const mediaUrlSecondRead = droparea.mediaList[0].url
|
|
218
|
+
|
|
219
|
+
assert.deepStrictEqual(mediaUrl, mediaUrlSecondRead)
|
|
220
|
+
assert.deepStrictEqual(
|
|
221
|
+
global.URL.createObjectURL.mock.calls.length,
|
|
222
|
+
callsBeforeMediaRead
|
|
223
|
+
)
|
|
87
224
|
|
|
88
|
-
|
|
225
|
+
droparea.querySelector('.ark-droparea__remove').click()
|
|
226
|
+
assert.deepStrictEqual(
|
|
227
|
+
global.URL.revokeObjectURL.mock.calls[0].arguments[0],
|
|
228
|
+
mediaUrl
|
|
229
|
+
)
|
|
230
|
+
})
|
|
89
231
|
|
|
90
|
-
|
|
232
|
+
it('returns early when handleDrop runs outside a droparea component', () => {
|
|
233
|
+
setup()
|
|
234
|
+
const preview = document.createElement('ark-droparea-preview')
|
|
235
|
+
const target = document.createElement('li')
|
|
236
|
+
preview.appendChild(target)
|
|
237
|
+
document.body.appendChild(preview)
|
|
91
238
|
|
|
92
|
-
|
|
93
|
-
|
|
239
|
+
assert.doesNotThrow(() => {
|
|
240
|
+
preview.handleDrop({ target })
|
|
241
|
+
})
|
|
242
|
+
})
|
|
94
243
|
|
|
95
|
-
|
|
244
|
+
it('returns early when removing a file that does not exist', () => {
|
|
245
|
+
setup()
|
|
246
|
+
container.innerHTML = /* html */ `
|
|
247
|
+
<ark-droparea></ark-droparea>
|
|
248
|
+
`
|
|
249
|
+
const droparea = container.querySelector('ark-droparea')
|
|
250
|
+
const preview = droparea.preview
|
|
251
|
+
const button = document.createElement('button')
|
|
252
|
+
const frame = document.createElement('li')
|
|
253
|
+
frame.appendChild(button)
|
|
254
|
+
preview.select('[data-preview-list]').appendChild(frame)
|
|
96
255
|
|
|
97
|
-
|
|
98
|
-
const endingNode = thumbnails[1]
|
|
256
|
+
const unknownFile = new File(['image'], 'unknown.png', { type: 'image/png' })
|
|
99
257
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
)
|
|
103
|
-
endingNode.dispatchEvent(
|
|
104
|
-
createBubbledEvent('dragend', { clientX: 0, clientY: 1 })
|
|
105
|
-
)
|
|
258
|
+
assert.doesNotThrow(() => {
|
|
259
|
+
preview.removeFile(unknownFile, { target: button })
|
|
106
260
|
})
|
|
261
|
+
assert.ok(preview.select('[data-preview-list]').contains(frame))
|
|
262
|
+
})
|
|
263
|
+
|
|
264
|
+
it('skips drag listeners for already-enabled items', () => {
|
|
265
|
+
setup()
|
|
266
|
+
const preview = document.createElement('ark-droparea-preview')
|
|
267
|
+
const item = document.createElement('li')
|
|
268
|
+
item.setAttribute('data-drag-enabled', '')
|
|
269
|
+
let calls = 0
|
|
270
|
+
const originalAddEventListener = item.addEventListener
|
|
271
|
+
item.addEventListener = function (...args) {
|
|
272
|
+
calls += 1
|
|
273
|
+
return originalAddEventListener.apply(this, args)
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
preview.enableDragItem(item)
|
|
277
|
+
|
|
278
|
+
assert.deepStrictEqual(calls, 0)
|
|
279
|
+
})
|
|
280
|
+
|
|
281
|
+
it('does nothing when revoking a file without an object URL', () => {
|
|
282
|
+
setup()
|
|
283
|
+
const preview = document.createElement('ark-droparea-preview')
|
|
284
|
+
const file = new File(['image'], 'Snoopy.png', { type: 'image/png' })
|
|
285
|
+
|
|
286
|
+
assert.doesNotThrow(() => {
|
|
287
|
+
preview.revokeFile(file)
|
|
288
|
+
})
|
|
289
|
+
})
|
|
290
|
+
|
|
291
|
+
it('renders a text preview in single mode and clears previous preview items', () => {
|
|
292
|
+
setup()
|
|
293
|
+
container.innerHTML = /* html */ `
|
|
294
|
+
<ark-droparea single></ark-droparea>
|
|
295
|
+
`
|
|
296
|
+
const droparea = container.querySelector('ark-droparea')
|
|
297
|
+
const dropZone = droparea.querySelector('.ark-droparea__form')
|
|
298
|
+
const firstFile = new File(['first'], 'first.txt', { type: 'text/plain' })
|
|
299
|
+
const secondFile = new File(['second'], 'second.txt', { type: 'text/plain' })
|
|
300
|
+
|
|
301
|
+
dropZone.dispatchEvent(createBubbledEvent('drop', {
|
|
302
|
+
dataTransfer: { files: [firstFile] }
|
|
303
|
+
}))
|
|
304
|
+
|
|
305
|
+
const firstPreviewItem = droparea.preview.querySelector('li')
|
|
306
|
+
assert.deepStrictEqual(firstPreviewItem.querySelector('p').textContent, 'first.txt')
|
|
307
|
+
assert.ok(firstPreviewItem.getAttribute('data').includes('mock://data/url/'))
|
|
308
|
+
assert.strictEqual(firstPreviewItem.getAttribute('index'), null)
|
|
309
|
+
|
|
310
|
+
dropZone.dispatchEvent(createBubbledEvent('drop', {
|
|
311
|
+
dataTransfer: { files: [secondFile] }
|
|
312
|
+
}))
|
|
313
|
+
|
|
314
|
+
const frames = droparea.preview.querySelectorAll('li')
|
|
315
|
+
assert.deepStrictEqual(frames.length, 1)
|
|
316
|
+
assert.deepStrictEqual(frames[0].querySelector('p').textContent, 'second.txt')
|
|
317
|
+
})
|
|
318
|
+
|
|
319
|
+
it('clearPreview tolerates missing preview list nodes', () => {
|
|
320
|
+
setup()
|
|
321
|
+
const preview = document.createElement('ark-droparea-preview')
|
|
322
|
+
const originalSelect = preview.select
|
|
323
|
+
preview.select = () => null
|
|
324
|
+
preview.toggleVisibility = () => {}
|
|
325
|
+
|
|
326
|
+
assert.doesNotThrow(() => {
|
|
327
|
+
preview.clearPreview()
|
|
328
|
+
})
|
|
329
|
+
|
|
330
|
+
preview.select = originalSelect
|
|
331
|
+
})
|
|
332
|
+
|
|
333
|
+
it('returns early when handleDrop receives a null target', () => {
|
|
334
|
+
setup()
|
|
335
|
+
const preview = document.createElement('ark-droparea-preview')
|
|
336
|
+
|
|
337
|
+
assert.doesNotThrow(() => {
|
|
338
|
+
preview.handleDrop({ target: null })
|
|
339
|
+
})
|
|
340
|
+
})
|
|
341
|
+
|
|
342
|
+
it('returns early when removeFile receives a null target', () => {
|
|
343
|
+
setup()
|
|
344
|
+
const preview = document.createElement('ark-droparea-preview')
|
|
345
|
+
const file = new File(['image'], 'Nully.png', { type: 'image/png' })
|
|
346
|
+
|
|
347
|
+
assert.doesNotThrow(() => {
|
|
348
|
+
preview.removeFile(file, { target: null })
|
|
349
|
+
})
|
|
350
|
+
})
|
|
351
|
+
|
|
352
|
+
it('revokeFile and revokeAllFiles tolerate missing revokeObjectURL', () => {
|
|
353
|
+
setup()
|
|
354
|
+
const preview = document.createElement('ark-droparea-preview')
|
|
355
|
+
const file = new File(['one'], 'one.txt', { type: 'text/plain' })
|
|
356
|
+
const secondFile = new File(['two'], 'two.txt', { type: 'text/plain' })
|
|
357
|
+
preview._objectUrls.set(file, 'mock://data/url/a')
|
|
358
|
+
preview._objectUrls.set(secondFile, 'mock://data/url/b')
|
|
359
|
+
|
|
360
|
+
const originalRevoke = global.URL.revokeObjectURL
|
|
361
|
+
global.URL.revokeObjectURL = undefined
|
|
362
|
+
|
|
363
|
+
try {
|
|
364
|
+
assert.doesNotThrow(() => {
|
|
365
|
+
preview.revokeFile(file)
|
|
366
|
+
preview.revokeAllFiles()
|
|
367
|
+
})
|
|
368
|
+
} finally {
|
|
369
|
+
global.URL.revokeObjectURL = originalRevoke
|
|
370
|
+
}
|
|
107
371
|
})
|
|
@@ -5,8 +5,21 @@ import './droparea-preview.js'
|
|
|
5
5
|
/** @import {DropareaPreview} from './droparea-preview.js' */
|
|
6
6
|
|
|
7
7
|
const tag = 'ark-droparea'
|
|
8
|
+
/**
|
|
9
|
+
* Drag-and-drop and file input area.
|
|
10
|
+
* Emits:
|
|
11
|
+
* - `alter` with current media list details.
|
|
12
|
+
*/
|
|
8
13
|
|
|
9
14
|
export class Droparea extends Component {
|
|
15
|
+
constructor () {
|
|
16
|
+
super()
|
|
17
|
+
this._onChange = this.onChange.bind(this)
|
|
18
|
+
this._onOpenInput = this.openInput.bind(this)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** @param {object} context
|
|
22
|
+
* @returns {this} */
|
|
10
23
|
init (context = {}) {
|
|
11
24
|
this.fileList = []
|
|
12
25
|
this.contextFiles = context.contextFiles || this.contextFiles || []
|
|
@@ -16,6 +29,7 @@ export class Droparea extends Component {
|
|
|
16
29
|
return super.init()
|
|
17
30
|
}
|
|
18
31
|
|
|
32
|
+
/** @returns {this} */
|
|
19
33
|
render () {
|
|
20
34
|
this._grabSlots()
|
|
21
35
|
this.content = /* html */ `
|
|
@@ -37,11 +51,15 @@ export class Droparea extends Component {
|
|
|
37
51
|
return super.render()
|
|
38
52
|
}
|
|
39
53
|
|
|
54
|
+
/** @returns {string[]} */
|
|
40
55
|
reflectedProperties () {
|
|
41
56
|
return ['size', 'accept', 'maxSize', 'title']
|
|
42
57
|
}
|
|
43
58
|
|
|
59
|
+
/** @returns {Promise<void>} */
|
|
44
60
|
async load () {
|
|
61
|
+
this._detachListeners()
|
|
62
|
+
|
|
45
63
|
this.dragDropEvents.forEach((eventName) => {
|
|
46
64
|
this.addEventListener(eventName, this.preventDefaults, false)
|
|
47
65
|
})
|
|
@@ -55,8 +73,8 @@ export class Droparea extends Component {
|
|
|
55
73
|
})
|
|
56
74
|
|
|
57
75
|
this.addEventListener('drop', this.handleDrop, false)
|
|
58
|
-
this._input
|
|
59
|
-
this.openButton
|
|
76
|
+
this._input?.addEventListener('change', this._onChange)
|
|
77
|
+
this.openButton?.addEventListener('click', this._onOpenInput)
|
|
60
78
|
|
|
61
79
|
/* istanbul ignore else */
|
|
62
80
|
if (this.contextFiles) {
|
|
@@ -64,25 +82,66 @@ export class Droparea extends Component {
|
|
|
64
82
|
}
|
|
65
83
|
}
|
|
66
84
|
|
|
85
|
+
/** @returns {void} */
|
|
86
|
+
disconnectedCallback () {
|
|
87
|
+
this._detachListeners()
|
|
88
|
+
super.disconnectedCallback()
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** @returns {void} */
|
|
92
|
+
_detachListeners () {
|
|
93
|
+
if (this.dragDropEvents) {
|
|
94
|
+
this.dragDropEvents.forEach((eventName) => {
|
|
95
|
+
this.removeEventListener(eventName, this.preventDefaults, false)
|
|
96
|
+
})
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (this.dragEvents) {
|
|
100
|
+
this.dragEvents.forEach((eventName) => {
|
|
101
|
+
this.removeEventListener(eventName, this.highlight, false)
|
|
102
|
+
})
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (this.dropEvents) {
|
|
106
|
+
this.dropEvents.forEach((eventName) => {
|
|
107
|
+
this.removeEventListener(eventName, this.unhighlight, false)
|
|
108
|
+
})
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
this.removeEventListener('drop', this.handleDrop, false)
|
|
112
|
+
this._input?.removeEventListener('change', this._onChange)
|
|
113
|
+
this.openButton?.removeEventListener('click', this._onOpenInput)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** @param {Event} event
|
|
117
|
+
* @returns {void} */
|
|
67
118
|
openInput (event) {
|
|
68
119
|
event.stopPropagation()
|
|
69
120
|
const input = this.select('[data-input]')
|
|
70
121
|
input.click()
|
|
71
122
|
}
|
|
72
123
|
|
|
124
|
+
/** @param {Event} event
|
|
125
|
+
* @returns {void} */
|
|
73
126
|
preventDefaults (event) {
|
|
74
127
|
event.preventDefault()
|
|
75
128
|
event.stopPropagation()
|
|
76
129
|
}
|
|
77
130
|
|
|
131
|
+
/** @param {Event} event
|
|
132
|
+
* @returns {void} */
|
|
78
133
|
highlight (event) {
|
|
79
134
|
this.dropZone.classList.add('highlight')
|
|
80
135
|
}
|
|
81
136
|
|
|
137
|
+
/** @param {Event} event
|
|
138
|
+
* @returns {void} */
|
|
82
139
|
unhighlight (event) {
|
|
83
140
|
this.dropZone.classList.remove('highlight')
|
|
84
141
|
}
|
|
85
142
|
|
|
143
|
+
/** @param {DragEvent} event
|
|
144
|
+
* @returns {void} */
|
|
86
145
|
handleDrop (event) {
|
|
87
146
|
event.stopPropagation()
|
|
88
147
|
const data = event.dataTransfer
|
|
@@ -90,13 +149,17 @@ export class Droparea extends Component {
|
|
|
90
149
|
this.handleFiles(files)
|
|
91
150
|
}
|
|
92
151
|
|
|
152
|
+
/** @param {Event} event
|
|
153
|
+
* @returns {void} */
|
|
93
154
|
onChange (event) {
|
|
94
155
|
event.stopPropagation()
|
|
95
|
-
const input = event.target
|
|
156
|
+
const input = /** @type {HTMLInputElement} */ (event.target)
|
|
96
157
|
const files = input.files
|
|
97
158
|
this.handleFiles(files)
|
|
98
159
|
}
|
|
99
160
|
|
|
161
|
+
/** @param {FileList|File[]} files
|
|
162
|
+
* @returns {void} */
|
|
100
163
|
handleFiles (files) {
|
|
101
164
|
if (this.single) {
|
|
102
165
|
files = [files[0]]
|
|
@@ -106,8 +169,9 @@ export class Droparea extends Component {
|
|
|
106
169
|
!this.preview.fileExists(files[0]) &&
|
|
107
170
|
this.maxSizeValidate(files[0])
|
|
108
171
|
) {
|
|
172
|
+
this.fileList[0] && this.preview.revokeFile(this.fileList[0])
|
|
109
173
|
this.fileList[0] = files[0]
|
|
110
|
-
this.preview.
|
|
174
|
+
this.preview.clearPreview()
|
|
111
175
|
this.preview.previewFile(files[0])
|
|
112
176
|
}
|
|
113
177
|
} else {
|
|
@@ -125,6 +189,10 @@ export class Droparea extends Component {
|
|
|
125
189
|
this.preview.dispatchAlterEvent()
|
|
126
190
|
}
|
|
127
191
|
|
|
192
|
+
/**
|
|
193
|
+
* @param {File[]|FileList} fileList
|
|
194
|
+
* @returns {boolean}
|
|
195
|
+
*/
|
|
128
196
|
validate (fileList) {
|
|
129
197
|
if (!this.accept || this.accept.length === 0) return true
|
|
130
198
|
const acceptList = this.accept.split(',').map(
|
|
@@ -148,16 +216,20 @@ export class Droparea extends Component {
|
|
|
148
216
|
return true
|
|
149
217
|
}
|
|
150
218
|
|
|
219
|
+
/** @param {File} file
|
|
220
|
+
* @returns {boolean} */
|
|
151
221
|
maxSizeValidate (file) {
|
|
152
222
|
return true
|
|
153
223
|
}
|
|
154
224
|
|
|
225
|
+
/** @returns {void} */
|
|
155
226
|
_grabSlots() {
|
|
156
227
|
const [fileInput] = [this.slots.general].flat()
|
|
157
228
|
this.fileInput = this.fileInput ?? fileInput
|
|
158
|
-
|
|
159
229
|
}
|
|
160
230
|
|
|
231
|
+
/** @param {HTMLInputElement|undefined} element
|
|
232
|
+
* @returns {HTMLInputElement} */
|
|
161
233
|
_buildFileInput (element) {
|
|
162
234
|
const input = element ?? document.createElement('input')
|
|
163
235
|
const attributes = [['class', `${tag}__input`],
|
|
@@ -166,14 +238,18 @@ export class Droparea extends Component {
|
|
|
166
238
|
return input
|
|
167
239
|
}
|
|
168
240
|
|
|
241
|
+
/** @returns {HTMLFormElement} */
|
|
169
242
|
get dropZone () {
|
|
170
|
-
return
|
|
243
|
+
return /** @type {HTMLFormElement} */ (
|
|
244
|
+
this.querySelector('.ark-droparea__form'))
|
|
171
245
|
}
|
|
172
246
|
|
|
247
|
+
/** @returns {DropareaPreview} */
|
|
173
248
|
get preview () {
|
|
174
249
|
return /** @type {DropareaPreview} */ (this.select('ark-droparea-preview'))
|
|
175
250
|
}
|
|
176
251
|
|
|
252
|
+
/** @returns {Array<{name:string,type:string,size:number,url:string}>} */
|
|
177
253
|
get mediaList () {
|
|
178
254
|
return this.preview.mediaList
|
|
179
255
|
}
|