@knowark/componarkjs 1.14.0 → 1.14.2

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.
Files changed (102) hide show
  1. package/README.md +57 -45
  2. package/lib/base/component/README.md +127 -0
  3. package/lib/base/component/component.js +127 -21
  4. package/lib/base/component/component.test.js +296 -3
  5. package/lib/base/component/index.js +3 -0
  6. package/lib/base/styles/index.js +4 -1
  7. package/lib/base/utils/define.js +2 -1
  8. package/lib/base/utils/format.js +12 -6
  9. package/lib/base/utils/helpers.js +31 -5
  10. package/lib/base/utils/index.js +1 -0
  11. package/lib/base/utils/slots.js +3 -2
  12. package/lib/base/utils/uuid.js +1 -1
  13. package/lib/components/audio/components/audio.js +17 -2
  14. package/lib/components/audio/index.js +1 -0
  15. package/lib/components/audio/styles/index.js +5 -1
  16. package/lib/components/camera/components/camera.js +10 -0
  17. package/lib/components/camera/index.js +1 -0
  18. package/lib/components/camera/styles/index.js +5 -1
  19. package/lib/components/capture/components/capture.js +18 -2
  20. package/lib/components/capture/index.js +1 -0
  21. package/lib/components/droparea/components/droparea-preview.js +58 -13
  22. package/lib/components/droparea/components/droparea-preview.test.js +82 -0
  23. package/lib/components/droparea/components/droparea.js +41 -2
  24. package/lib/components/droparea/index.js +1 -0
  25. package/lib/components/droparea/styles/index.js +5 -1
  26. package/lib/components/emit/components/emit.js +11 -1
  27. package/lib/components/emit/index.js +1 -0
  28. package/lib/components/index.js +2 -1
  29. package/lib/components/list/components/item.js +6 -0
  30. package/lib/components/list/components/list.js +18 -4
  31. package/lib/components/list/index.js +1 -0
  32. package/lib/components/paginator/components/paginator.js +34 -8
  33. package/lib/components/paginator/index.js +1 -0
  34. package/lib/components/paginator/styles/index.js +5 -1
  35. package/lib/components/spinner/components/spinner.js +10 -0
  36. package/lib/components/spinner/index.js +1 -0
  37. package/lib/components/spinner/styles/index.js +5 -1
  38. package/lib/components/splitview/components/splitview.detail.js +10 -1
  39. package/lib/components/splitview/components/splitview.js +18 -3
  40. package/lib/components/splitview/components/splitview.master.js +10 -0
  41. package/lib/components/splitview/index.js +1 -0
  42. package/lib/components/translate/components/translate.js +42 -11
  43. package/lib/components/translate/components/translate.test.js +169 -1
  44. package/lib/components/translate/index.js +1 -0
  45. package/lib/index.js +3 -0
  46. package/package.json +2 -1
  47. package/tsconfig.json +1 -1
  48. package/types/base/component/component.d.ts +43 -8
  49. package/types/base/component/component.d.ts.map +1 -1
  50. package/types/base/component/index.d.ts +4 -6
  51. package/types/base/component/index.d.ts.map +1 -1
  52. package/types/base/styles/index.d.ts +3 -2
  53. package/types/base/styles/index.d.ts.map +1 -1
  54. package/types/base/utils/define.d.ts +3 -2
  55. package/types/base/utils/define.d.ts.map +1 -1
  56. package/types/base/utils/format.d.ts +12 -6
  57. package/types/base/utils/format.d.ts.map +1 -1
  58. package/types/base/utils/helpers.d.ts +27 -7
  59. package/types/base/utils/helpers.d.ts.map +1 -1
  60. package/types/base/utils/slots.d.ts +8 -10
  61. package/types/base/utils/slots.d.ts.map +1 -1
  62. package/types/base/utils/uuid.d.ts +1 -1
  63. package/types/base/utils/uuid.d.ts.map +1 -1
  64. package/types/components/audio/components/audio.d.ts +23 -9
  65. package/types/components/audio/components/audio.d.ts.map +1 -1
  66. package/types/components/audio/styles/index.d.ts +3 -2
  67. package/types/components/audio/styles/index.d.ts.map +1 -1
  68. package/types/components/camera/components/camera.d.ts +11 -3
  69. package/types/components/camera/components/camera.d.ts.map +1 -1
  70. package/types/components/camera/styles/index.d.ts +3 -2
  71. package/types/components/camera/styles/index.d.ts.map +1 -1
  72. package/types/components/capture/components/capture.d.ts +23 -3
  73. package/types/components/capture/components/capture.d.ts.map +1 -1
  74. package/types/components/droparea/components/droparea-preview.d.ts +64 -11
  75. package/types/components/droparea/components/droparea-preview.d.ts.map +1 -1
  76. package/types/components/droparea/components/droparea.d.ts +58 -13
  77. package/types/components/droparea/components/droparea.d.ts.map +1 -1
  78. package/types/components/droparea/styles/index.d.ts +3 -2
  79. package/types/components/droparea/styles/index.d.ts.map +1 -1
  80. package/types/components/emit/components/emit.d.ts +15 -3
  81. package/types/components/emit/components/emit.d.ts.map +1 -1
  82. package/types/components/list/components/item.d.ts +8 -1
  83. package/types/components/list/components/item.d.ts.map +1 -1
  84. package/types/components/list/components/list.d.ts +23 -5
  85. package/types/components/list/components/list.d.ts.map +1 -1
  86. package/types/components/paginator/components/paginator.d.ts +32 -8
  87. package/types/components/paginator/components/paginator.d.ts.map +1 -1
  88. package/types/components/paginator/styles/index.d.ts +3 -2
  89. package/types/components/paginator/styles/index.d.ts.map +1 -1
  90. package/types/components/spinner/components/spinner.d.ts +14 -3
  91. package/types/components/spinner/components/spinner.d.ts.map +1 -1
  92. package/types/components/spinner/styles/index.d.ts +3 -2
  93. package/types/components/spinner/styles/index.d.ts.map +1 -1
  94. package/types/components/splitview/components/splitview.d.ts +22 -4
  95. package/types/components/splitview/components/splitview.d.ts.map +1 -1
  96. package/types/components/splitview/components/splitview.detail.d.ts +12 -2
  97. package/types/components/splitview/components/splitview.detail.d.ts.map +1 -1
  98. package/types/components/splitview/components/splitview.master.d.ts +12 -1
  99. package/types/components/splitview/components/splitview.master.d.ts.map +1 -1
  100. package/types/components/translate/components/translate.d.ts +44 -10
  101. package/types/components/translate/components/translate.d.ts.map +1 -1
  102. package/lib/base/component/README.rst +0 -113
@@ -64,6 +64,44 @@ class PlainLoadComponent extends Component {
64
64
  }
65
65
  Component.define('plain-load-component', PlainLoadComponent)
66
66
 
67
+ class StringLoadComponent extends Component {
68
+ load () {
69
+ throw 'String Load Error!'
70
+ }
71
+ }
72
+ Component.define('string-load-component', StringLoadComponent)
73
+
74
+ class CleanupComponent extends Component {
75
+ init (context = {}) {
76
+ this.cleanupCalls = 0
77
+ return super.init(context)
78
+ }
79
+
80
+ render () {
81
+ return super.render()
82
+ }
83
+ }
84
+ Component.define('cleanup-component', CleanupComponent)
85
+
86
+ class OptimizedListenerComponent extends Component {
87
+ init (context = {}) {
88
+ this.clicks = 0
89
+ return super.init(context)
90
+ }
91
+
92
+ render () {
93
+ if (!this.querySelector('button')) {
94
+ this.content = '<button listen on-click="onClick">Click me</button>'
95
+ }
96
+ return super.render()
97
+ }
98
+
99
+ onClick () {
100
+ this.clicks += 1
101
+ }
102
+ }
103
+ Component.define('optimized-listener-component', OptimizedListenerComponent)
104
+
67
105
  let container = null
68
106
  let component = null
69
107
 
@@ -166,6 +204,10 @@ it('catches and re-raises connectedCallback errors', async () => {
166
204
  const component = /** @type {Component} */ (
167
205
  document.createElement('mock-component'))
168
206
  const consoleErrorMock = mock.method(console, 'error', () => {})
207
+ let errorEvent = null
208
+ component.addEventListener('error', (event) => {
209
+ errorEvent = event
210
+ })
169
211
  component.render = () => {
170
212
  throw new Error('Render Error!')
171
213
  }
@@ -174,14 +216,72 @@ it('catches and re-raises connectedCallback errors', async () => {
174
216
  component.connectedCallback()
175
217
  } catch (error) {
176
218
  assert.deepStrictEqual(error.message, 'Render Error!')
219
+ assert.deepStrictEqual(errorEvent.detail.phase, 'init-render')
220
+ assert.deepStrictEqual(errorEvent.detail.component, 'MOCK-COMPONENT')
177
221
  } finally {
178
222
  consoleErrorMock.mock.restore()
179
223
  }
180
224
  })
181
225
 
182
- it('emits an error event when async load fails', async () => {
183
- setup()
184
- const component = document.createElement('async-load-component')
226
+ it('creates events without CustomEvent and keeps event details', () => {
227
+ setup()
228
+ const component = container.querySelector('mock-component')
229
+ const previousCustomEvent = globalThis.CustomEvent
230
+
231
+ try {
232
+ globalThis.CustomEvent = undefined
233
+ let detail = null
234
+ component.addEventListener('fallback-emit', (event) => {
235
+ detail = event.detail
236
+ })
237
+
238
+ component.emit('fallback-emit', { ready: true })
239
+
240
+ assert.deepStrictEqual(detail, { ready: true })
241
+ } finally {
242
+ globalThis.CustomEvent = previousCustomEvent
243
+ }
244
+ })
245
+
246
+ it('creates events from the component global window Event fallback', () => {
247
+ setup()
248
+ const component = document.createElement('mock-component')
249
+ const previousGlobal = component.global
250
+
251
+ try {
252
+ component.global = {
253
+ document: {
254
+ defaultView: {
255
+ Event: globalThis.Event
256
+ }
257
+ }
258
+ }
259
+
260
+ const event = component._createEvent('context-window-event', { ready: true })
261
+
262
+ assert.deepStrictEqual(event.type, 'context-window-event')
263
+ assert.deepStrictEqual(event.detail.ready, true)
264
+ assert.deepStrictEqual(typeof event.stopPropagation, 'function')
265
+ } finally {
266
+ component.global = previousGlobal
267
+ }
268
+ })
269
+
270
+ it('creates events from globalThis CustomEvent when component global has no window objects', () => {
271
+ setup()
272
+ const component = document.createElement('mock-component')
273
+
274
+ component.global = {}
275
+ const event = component._createEvent('global-custom-event', { ready: true })
276
+
277
+ assert.deepStrictEqual(event.type, 'global-custom-event')
278
+ assert.deepStrictEqual(event.detail.ready, true)
279
+ assert.deepStrictEqual(event instanceof globalThis.CustomEvent, true)
280
+ })
281
+
282
+ it('emits an error event when async load fails', async () => {
283
+ setup()
284
+ const component = document.createElement('async-load-component')
185
285
  let errorEvent = null
186
286
 
187
287
  component.addEventListener('error', (event) => {
@@ -194,6 +294,8 @@ it('emits an error event when async load fails', async () => {
194
294
 
195
295
  assert.ok(errorEvent)
196
296
  assert.deepStrictEqual(errorEvent.detail.message, 'Async Load Error!')
297
+ assert.deepStrictEqual(errorEvent.detail.phase, 'load')
298
+ assert.deepStrictEqual(errorEvent.detail.component, 'ASYNC-LOAD-COMPONENT')
197
299
  })
198
300
 
199
301
  it('emits an error event and throws when sync load fails', () => {
@@ -211,6 +313,176 @@ it('emits an error event and throws when sync load fails', () => {
211
313
 
212
314
  assert.ok(errorEvent)
213
315
  assert.deepStrictEqual(errorEvent.detail.message, 'Sync Load Error!')
316
+ assert.deepStrictEqual(errorEvent.detail.phase, 'load')
317
+ assert.deepStrictEqual(errorEvent.detail.component, 'SYNC-LOAD-COMPONENT')
318
+ })
319
+
320
+ it('coerces non-error load failures into errors with metadata', () => {
321
+ setup()
322
+ const component = document.createElement('string-load-component')
323
+ let errorEvent = null
324
+
325
+ component.addEventListener('error', (event) => {
326
+ errorEvent = event
327
+ })
328
+
329
+ assert.throws(() => {
330
+ component.connectedCallback()
331
+ }, /String Load Error!/)
332
+
333
+ assert.ok(errorEvent)
334
+ assert.deepStrictEqual(errorEvent.detail.message, 'String Load Error!')
335
+ assert.deepStrictEqual(errorEvent.detail.phase, 'load')
336
+ assert.deepStrictEqual(errorEvent.detail.component, 'STRING-LOAD-COMPONENT')
337
+ })
338
+
339
+ it('creates resolve events without CustomEvent and still resolves', () => {
340
+ setup()
341
+ const component = document.createElement('mock-component')
342
+ const previousCustomEvent = globalThis.CustomEvent
343
+
344
+ try {
345
+ globalThis.CustomEvent = undefined
346
+ let resource = null
347
+ component.addEventListener('resolve', (event) => {
348
+ resource = event.detail.resource
349
+ })
350
+
351
+ const result = component.resolve('dependency')
352
+
353
+ assert.deepStrictEqual(resource, 'dependency')
354
+ assert.strictEqual(result, undefined)
355
+ } finally {
356
+ globalThis.CustomEvent = previousCustomEvent
357
+ }
358
+ })
359
+
360
+ it('creates fallback events when neither CustomEvent nor Event are available', () => {
361
+ setup()
362
+ const component = document.createElement('mock-component')
363
+ const previousCustomEvent = globalThis.CustomEvent
364
+ const previousEvent = globalThis.Event
365
+
366
+ try {
367
+ globalThis.CustomEvent = undefined
368
+ globalThis.Event = undefined
369
+
370
+ component.global = {}
371
+ const event = component._createEvent('fallback', { ready: true })
372
+
373
+ assert.deepStrictEqual(event.type, 'fallback')
374
+ assert.deepStrictEqual(event.detail, { ready: true })
375
+ assert.deepStrictEqual(typeof event.stopPropagation, 'function')
376
+ assert.deepStrictEqual(typeof event.preventDefault, 'function')
377
+ assert.doesNotThrow(() => event.stopPropagation())
378
+ assert.doesNotThrow(() => event.preventDefault())
379
+ } finally {
380
+ globalThis.CustomEvent = previousCustomEvent
381
+ globalThis.Event = previousEvent
382
+ }
383
+ })
384
+
385
+ it('falls back to globalThis when component global is missing', () => {
386
+ setup()
387
+ const component = document.createElement('mock-component')
388
+ const previousCustomEvent = globalThis.CustomEvent
389
+
390
+ try {
391
+ globalThis.CustomEvent = undefined
392
+ component.global = null
393
+
394
+ const event = component._createEvent('global-fallback', { ready: true })
395
+
396
+ assert.deepStrictEqual(event.type, 'global-fallback')
397
+ assert.deepStrictEqual(event.detail.ready, true)
398
+ } finally {
399
+ globalThis.CustomEvent = previousCustomEvent
400
+ }
401
+ })
402
+
403
+ it('registers and runs cleanup callbacks on disconnect', () => {
404
+ setup()
405
+ const component = document.createElement('cleanup-component')
406
+ container.appendChild(component)
407
+
408
+ let cleanupCalls = 0
409
+ component.registerCleanup(() => {
410
+ cleanupCalls += 1
411
+ })
412
+ component.registerCleanup(() => {
413
+ cleanupCalls += 1
414
+ })
415
+
416
+ component.disconnectedCallback()
417
+
418
+ assert.deepStrictEqual(cleanupCalls, 2)
419
+ })
420
+
421
+ it('supports unregistering cleanup callbacks', () => {
422
+ setup()
423
+ const component = document.createElement('cleanup-component')
424
+ container.appendChild(component)
425
+
426
+ let cleanupCalls = 0
427
+ const unregister = component.registerCleanup(() => {
428
+ cleanupCalls += 1
429
+ })
430
+ unregister()
431
+ unregister()
432
+
433
+ component.disconnectedCallback()
434
+
435
+ assert.deepStrictEqual(cleanupCalls, 0)
436
+ })
437
+
438
+ it('supports disconnected init fast-path when local state is already set', () => {
439
+ setup()
440
+ const component = document.createElement('mock-component')
441
+ component.local = { preset: true }
442
+ const initSpy = mock.method(component, 'init')
443
+ container.appendChild(component)
444
+
445
+ assert.deepStrictEqual(initSpy.mock.calls.length, 0)
446
+ initSpy.mock.restore()
447
+ })
448
+
449
+ it('returns unchanged values when enhanced error details are absent', () => {
450
+ setup()
451
+ const component = document.createElement('cleanup-component')
452
+
453
+ const result = component._enhanceError(undefined, 'init-render')
454
+
455
+ assert.strictEqual(result, undefined)
456
+ })
457
+
458
+ it('accepts non-function cleanup callbacks as no-op registrations', () => {
459
+ setup()
460
+ const component = document.createElement('cleanup-component')
461
+
462
+ const unregister = component.registerCleanup()
463
+
464
+ assert.doesNotThrow(() => unregister())
465
+ })
466
+
467
+ it('emits an error event when a cleanup callback throws', () => {
468
+ setup()
469
+ const component = document.createElement('cleanup-component')
470
+ container.appendChild(component)
471
+ let errorEvent = null
472
+
473
+ component.addEventListener('error', (event) => {
474
+ errorEvent = event
475
+ })
476
+ component.registerCleanup(() => {
477
+ throw new Error('Cleanup failed')
478
+ })
479
+
480
+ component.disconnectedCallback()
481
+
482
+ assert.ok(errorEvent)
483
+ assert.deepStrictEqual(errorEvent.detail.message, 'Cleanup failed')
484
+ assert.deepStrictEqual(errorEvent.detail.phase, 'cleanup')
485
+ assert.deepStrictEqual(errorEvent.detail.component, 'CLEANUP-COMPONENT')
214
486
  })
215
487
 
216
488
  it('supports load implementations that do not return promises', () => {
@@ -611,3 +883,24 @@ it('provides a styleNames utility function for setting styles', () => {
611
883
 
612
884
  assert.deepStrictEqual(result, 'background-primary shadow-small')
613
885
  })
886
+
887
+ it('reuses listener bindings when content does not change', () => {
888
+ setup()
889
+ container.innerHTML = `
890
+ <optimized-listener-component></optimized-listener-component>
891
+ `
892
+ const component = container.querySelector('optimized-listener-component')
893
+
894
+ component.render()
895
+ component.querySelector('button').click()
896
+
897
+ component.render()
898
+ component.querySelector('button').click()
899
+
900
+ component.content = '<button listen on-click="onClick">Click me</button>'
901
+ component.render()
902
+ component.querySelector('button').click()
903
+
904
+ assert.deepStrictEqual(component.clicks, 3)
905
+ assert.deepStrictEqual(component._needsBinding, false)
906
+ })
@@ -1,3 +1,6 @@
1
1
  export { Component } from './component.js'
2
+
3
+ /** @type {typeof String.raw} */
2
4
  export const css = String.raw
5
+ /** @type {typeof String.raw} */
3
6
  export const html = String.raw
@@ -1,3 +1,6 @@
1
1
  import styles from './styles.js'
2
2
 
3
- export default styles
3
+ /** @type {string} */
4
+ const stylesText = styles
5
+
6
+ export default stylesText
@@ -5,7 +5,8 @@ const fallbackRegistry = new Map()
5
5
 
6
6
  /** @param {string} tag
7
7
  * @param {CustomElementConstructor} element
8
- * @param {string} styles **/
8
+ * @param {string} [styles]
9
+ * @returns {CSSStyleSheet|HTMLStyleElement|undefined} */
9
10
  export function define (tag, element, styles = '') {
10
11
  const definedElement = globalThis.customElements.get(tag)
11
12
  if (!definedElement) {
@@ -1,13 +1,17 @@
1
1
  /**
2
- * Convert Strings from camelCase to kebab-case
3
- * @param {string} input @returns {string} */
2
+ * Convert Strings from camelCase to kebab-case.
3
+ * @param {string} input
4
+ * @returns {string}
5
+ */
4
6
  export function camelToKebab (input) {
5
7
  return input.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase()
6
8
  }
7
9
 
8
10
  /**
9
- * Convert Strings from kebab-case to camelCase
10
- * @param {string} input @returns {string} */
11
+ * Convert Strings from kebab-case to camelCase.
12
+ * @param {string} input
13
+ * @returns {string}
14
+ */
11
15
  export function kebabToCamel (input) {
12
16
  return input.replace(/-([a-z])/g, function (g) {
13
17
  return g[1].toUpperCase()
@@ -15,8 +19,10 @@ export function kebabToCamel (input) {
15
19
  }
16
20
 
17
21
  /**
18
- * Convert Strings from snake to camelCase
19
- * @param {string} input @returns {string} */
22
+ * Convert Strings from snake to camelCase.
23
+ * @param {string} input
24
+ * @returns {string}
25
+ */
20
26
  export function snakeToCamel (input) {
21
27
  return input.replace(/_([a-z])/g, function (g) {
22
28
  return g[1].toUpperCase()
@@ -1,6 +1,13 @@
1
+ /**
2
+ * @import { Component } from '../component/component.js'
3
+ */
1
4
  import { camelToKebab } from './format.js'
2
5
 
3
- /** @param {HTMLElement} self */
6
+ /**
7
+ * Binds declarative event handlers in descendant nodes for a component.
8
+ * @param {Component|HTMLElement} self
9
+ * @returns {void}
10
+ */
4
11
  export function listen (self) {
5
12
  /** @ts-ignore */
6
13
  const binding = self.binding
@@ -62,7 +69,11 @@ export function listen (self) {
62
69
  }
63
70
  }
64
71
 
65
- /** @param {HTMLElement} self */
72
+ /**
73
+ * Resolve dependencies from descendants using `resolve` events.
74
+ * @param {Component|HTMLElement} self
75
+ * @returns {void}
76
+ */
66
77
  function provide (self) {
67
78
  /** @ts-ignore */
68
79
  if (!self.provide) return
@@ -102,7 +113,13 @@ function transform (pipe, value) {
102
113
  }[pipe?.toLowerCase() || 'string'](value)
103
114
  }
104
115
 
105
- /** @param {object} object @param {string} path @param {any} value */
116
+ /**
117
+ * Sets a nested property value by object path.
118
+ * @param {unknown} object
119
+ * @param {string} path
120
+ * @param {any} value
121
+ * @returns {void}
122
+ */
106
123
  export function set (object, path, value) {
107
124
  const pathArray = path.match(/([^[.\]])+/g)
108
125
  if (!pathArray?.length) return
@@ -114,7 +131,13 @@ export function set (object, path, value) {
114
131
  }, object)
115
132
  }
116
133
 
117
- /** @param {object} object @param {string} path @param {any} fallback */
134
+ /**
135
+ * Reads a nested property value by path.
136
+ * @param {unknown} object
137
+ * @param {string} path
138
+ * @param {any} fallback
139
+ * @returns {any}
140
+ */
118
141
  export function get (object, path, fallback) {
119
142
  const pathArray = path.match(/([^[.\]])+/g)
120
143
  if (!pathArray?.length) return fallback
@@ -125,7 +148,10 @@ export function get (object, path, fallback) {
125
148
  return value === undefined ? fallback : value
126
149
  }
127
150
 
128
- /** @param {object} object @return {string} */
151
+ /** @param {object} object
152
+ * @return {string}
153
+ * @description Returns truthy CSS class names from object values.
154
+ */
129
155
  export function keys (object) {
130
156
  return Object.keys(object).filter(
131
157
  key => Boolean(object[key])).join(' ')
@@ -1,3 +1,4 @@
1
+ /** Utility helpers for component internals and runtime bindings. */
1
2
  export * from './define.js'
2
3
  export * from './format.js'
3
4
  export * from './helpers.js'
@@ -1,7 +1,8 @@
1
1
  /**
2
+ * Groups child nodes by slot name.
2
3
  * @param {HTMLElement} container
3
- * @return {Object<string, Array<HTMLElement>>}
4
- * */
4
+ * @returns {Record<string, HTMLElement[]>}
5
+ */
5
6
  export function getSlots (container) {
6
7
  const slots = { general: [] }
7
8
 
@@ -1,4 +1,4 @@
1
- /** @returns {string} */
1
+ /** @returns {string} Generated RFC-4122-like identifier. */
2
2
  export function uuid () {
3
3
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(
4
4
  /[xy]/g, function (c) {
@@ -2,8 +2,16 @@ import { Component } from '#base/index.js'
2
2
  import styles from '../styles/index.js'
3
3
 
4
4
  const tag = 'ark-audio'
5
+ /**
6
+ * Audio recorder component.
7
+ * Emits:
8
+ * - `error` with `Error` detail on capture/rendering issues.
9
+ */
5
10
  export class Audio extends Component {
11
+ /** @param {object} context
12
+ * @returns {this} */
6
13
  init (context = {}) {
14
+ /** @type {'idle'|'recording'|'done'} */
7
15
  this.status = 'idle'
8
16
  this.dataURL = null
9
17
  this.timerId = null
@@ -18,6 +26,7 @@ export class Audio extends Component {
18
26
  return ['status']
19
27
  }
20
28
 
29
+ /** @returns {this} */
21
30
  render () {
22
31
  if (this.status === 'done') {
23
32
  this.content = `
@@ -45,7 +54,8 @@ export class Audio extends Component {
45
54
  return super.render()
46
55
  }
47
56
 
48
- /** @param {Event} event */
57
+ /** @param {Event} event
58
+ * @returns {Promise<void>} */
49
59
  async start (event) {
50
60
  event.stopPropagation()
51
61
  this.status = 'recording'
@@ -60,7 +70,8 @@ export class Audio extends Component {
60
70
  this.recorder.start()
61
71
  }
62
72
 
63
- /** @param {Event} event */
73
+ /** @param {Event} event
74
+ * @returns {void} */
64
75
  stop (event) {
65
76
  event.stopPropagation()
66
77
  this.status = 'done'
@@ -70,6 +81,7 @@ export class Audio extends Component {
70
81
  this.render()
71
82
  }
72
83
 
84
+ /** @returns {void} */
73
85
  reset () {
74
86
  clearInterval(this.timerId)
75
87
  this._revokeObjectURL()
@@ -80,6 +92,7 @@ export class Audio extends Component {
80
92
  this.recorder = null
81
93
  }
82
94
 
95
+ /** @returns {void} */
83
96
  disconnectedCallback () {
84
97
  clearInterval(this.timerId)
85
98
  this.recorder?.stream?.getTracks?.().forEach(track => track.stop())
@@ -87,6 +100,7 @@ export class Audio extends Component {
87
100
  super.disconnectedCallback()
88
101
  }
89
102
 
103
+ /** @returns {ReturnType<typeof setInterval>} Interval handle. */
90
104
  _time () {
91
105
  let count = 0
92
106
  return setInterval(() => {
@@ -113,6 +127,7 @@ export class Audio extends Component {
113
127
  reader.onloadend = () => { this.dataURL = reader.result }
114
128
  }
115
129
 
130
+ /** @returns {void} */
116
131
  _revokeObjectURL () {
117
132
  if (!this.objectURL) return
118
133
  this.global.URL.revokeObjectURL?.(this.objectURL)
@@ -1 +1,2 @@
1
+ /** Audio recording component. */
1
2
  export { Audio } from './components/audio.js'
@@ -1,2 +1,6 @@
1
1
  import styles from './ark.css.js'
2
- export default styles
2
+
3
+ /** @type {string} */
4
+ const stylesText = styles
5
+
6
+ export default stylesText
@@ -2,7 +2,12 @@ import { Component } from '#base/index.js'
2
2
  import styles from '../styles/index.js'
3
3
 
4
4
  const tag = 'ark-camera'
5
+ /**
6
+ * Camera capture component.
7
+ */
5
8
  export class Camera extends Component {
9
+ /** @param {object} context
10
+ * @returns {this} */
6
11
  init (context = {}) {
7
12
  this.width = this.width || context.width || 320
8
13
  this.height = this.height || context.height || 320
@@ -47,6 +52,7 @@ export class Camera extends Component {
47
52
  return canvas.toDataURL('image/jpg')
48
53
  }
49
54
 
55
+ /** @returns {Promise<void>} */
50
56
  async start () {
51
57
  const stream = await this.global.navigator.mediaDevices.getUserMedia({
52
58
  video: {
@@ -60,18 +66,22 @@ export class Camera extends Component {
60
66
  this.video.srcObject = stream
61
67
  }
62
68
 
69
+ /** @returns {void} */
63
70
  stop () {
64
71
  // @ts-ignore
65
72
  const tracks = this.video.srcObject ? this.video.srcObject.getTracks() : []
66
73
  tracks.forEach(track => track.stop())
67
74
  }
68
75
 
76
+ /** @param {string} facingMode
77
+ * @returns {Promise<void>} */
69
78
  async setCameraOrientation (facingMode) {
70
79
  this.stop()
71
80
  this.facingMode = facingMode
72
81
  await this.start()
73
82
  }
74
83
 
84
+ /** @returns {void} */
75
85
  disconnectedCallback () {
76
86
  this.stop()
77
87
  super.disconnectedCallback()
@@ -1 +1,2 @@
1
+ /** Camera capture component. */
1
2
  export { Camera } from './components/camera.js'
@@ -1,2 +1,6 @@
1
1
  import styles from './ark.css.js'
2
- export default styles
2
+
3
+ /** @type {string} */
4
+ const stylesText = styles
5
+
6
+ export default stylesText