@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.
Files changed (118) hide show
  1. package/README.md +57 -45
  2. package/lib/base/component/component.js +142 -20
  3. package/lib/base/component/component.test.js +753 -374
  4. package/lib/base/component/index.js +3 -0
  5. package/lib/base/styles/index.js +4 -1
  6. package/lib/base/utils/define.js +30 -7
  7. package/lib/base/utils/define.test.js +129 -42
  8. package/lib/base/utils/format.js +12 -6
  9. package/lib/base/utils/format.test.js +16 -16
  10. package/lib/base/utils/helpers.js +42 -9
  11. package/lib/base/utils/helpers.test.js +134 -115
  12. package/lib/base/utils/index.js +1 -0
  13. package/lib/base/utils/slots.js +3 -2
  14. package/lib/base/utils/slots.test.js +38 -38
  15. package/lib/base/utils/uuid.js +1 -1
  16. package/lib/base/utils/uuid.test.js +13 -13
  17. package/lib/components/audio/components/audio.js +36 -3
  18. package/lib/components/audio/components/audio.test.js +120 -90
  19. package/lib/components/audio/index.js +1 -0
  20. package/lib/components/audio/styles/index.js +5 -1
  21. package/lib/components/camera/components/camera.js +15 -0
  22. package/lib/components/camera/components/camera.test.js +96 -91
  23. package/lib/components/camera/index.js +1 -0
  24. package/lib/components/camera/styles/index.js +5 -1
  25. package/lib/components/capture/components/capture.js +48 -4
  26. package/lib/components/capture/components/capture.test.js +165 -97
  27. package/lib/components/capture/index.js +1 -0
  28. package/lib/components/droparea/components/droparea-preview.js +114 -19
  29. package/lib/components/droparea/components/droparea-preview.test.js +344 -80
  30. package/lib/components/droparea/components/droparea.js +82 -6
  31. package/lib/components/droparea/components/droparea.test.js +309 -299
  32. package/lib/components/droparea/index.js +1 -0
  33. package/lib/components/droparea/styles/index.js +5 -1
  34. package/lib/components/emit/components/emit.js +34 -4
  35. package/lib/components/emit/components/emit.test.js +192 -134
  36. package/lib/components/emit/index.js +1 -0
  37. package/lib/components/index.js +2 -1
  38. package/lib/components/list/components/item.js +6 -0
  39. package/lib/components/list/components/item.test.js +69 -68
  40. package/lib/components/list/components/list.js +51 -7
  41. package/lib/components/list/components/list.test.js +358 -227
  42. package/lib/components/list/index.js +1 -0
  43. package/lib/components/paginator/components/paginator.js +34 -8
  44. package/lib/components/paginator/components/paginator.test.js +146 -143
  45. package/lib/components/paginator/index.js +1 -0
  46. package/lib/components/paginator/styles/index.js +5 -1
  47. package/lib/components/spinner/components/spinner.js +10 -0
  48. package/lib/components/spinner/components/spinner.test.js +36 -41
  49. package/lib/components/spinner/index.js +1 -0
  50. package/lib/components/spinner/styles/index.js +5 -1
  51. package/lib/components/splitview/components/splitview.detail.js +10 -1
  52. package/lib/components/splitview/components/splitview.detail.test.js +75 -73
  53. package/lib/components/splitview/components/splitview.js +54 -11
  54. package/lib/components/splitview/components/splitview.master.js +37 -2
  55. package/lib/components/splitview/components/splitview.master.test.js +52 -52
  56. package/lib/components/splitview/components/splitview.test.js +135 -31
  57. package/lib/components/splitview/index.js +1 -0
  58. package/lib/components/translate/components/translate.js +65 -14
  59. package/lib/components/translate/components/translate.test.js +658 -131
  60. package/lib/components/translate/index.js +1 -0
  61. package/lib/index.js +3 -0
  62. package/package.json +4 -27
  63. package/scripts/node-test-setup.js +94 -0
  64. package/tsconfig.json +1 -1
  65. package/types/base/component/component.d.ts +43 -8
  66. package/types/base/component/component.d.ts.map +1 -1
  67. package/types/base/component/index.d.ts +4 -6
  68. package/types/base/component/index.d.ts.map +1 -1
  69. package/types/base/styles/index.d.ts +3 -2
  70. package/types/base/styles/index.d.ts.map +1 -1
  71. package/types/base/utils/define.d.ts +3 -2
  72. package/types/base/utils/define.d.ts.map +1 -1
  73. package/types/base/utils/format.d.ts +12 -6
  74. package/types/base/utils/format.d.ts.map +1 -1
  75. package/types/base/utils/helpers.d.ts +27 -7
  76. package/types/base/utils/helpers.d.ts.map +1 -1
  77. package/types/base/utils/slots.d.ts +8 -10
  78. package/types/base/utils/slots.d.ts.map +1 -1
  79. package/types/base/utils/uuid.d.ts +1 -1
  80. package/types/base/utils/uuid.d.ts.map +1 -1
  81. package/types/components/audio/components/audio.d.ts +23 -9
  82. package/types/components/audio/components/audio.d.ts.map +1 -1
  83. package/types/components/audio/styles/index.d.ts +3 -2
  84. package/types/components/audio/styles/index.d.ts.map +1 -1
  85. package/types/components/camera/components/camera.d.ts +11 -3
  86. package/types/components/camera/components/camera.d.ts.map +1 -1
  87. package/types/components/camera/styles/index.d.ts +3 -2
  88. package/types/components/camera/styles/index.d.ts.map +1 -1
  89. package/types/components/capture/components/capture.d.ts +23 -3
  90. package/types/components/capture/components/capture.d.ts.map +1 -1
  91. package/types/components/droparea/components/droparea-preview.d.ts +64 -11
  92. package/types/components/droparea/components/droparea-preview.d.ts.map +1 -1
  93. package/types/components/droparea/components/droparea.d.ts +58 -13
  94. package/types/components/droparea/components/droparea.d.ts.map +1 -1
  95. package/types/components/droparea/styles/index.d.ts +3 -2
  96. package/types/components/droparea/styles/index.d.ts.map +1 -1
  97. package/types/components/emit/components/emit.d.ts +15 -3
  98. package/types/components/emit/components/emit.d.ts.map +1 -1
  99. package/types/components/list/components/item.d.ts +8 -1
  100. package/types/components/list/components/item.d.ts.map +1 -1
  101. package/types/components/list/components/list.d.ts +23 -5
  102. package/types/components/list/components/list.d.ts.map +1 -1
  103. package/types/components/paginator/components/paginator.d.ts +32 -8
  104. package/types/components/paginator/components/paginator.d.ts.map +1 -1
  105. package/types/components/paginator/styles/index.d.ts +3 -2
  106. package/types/components/paginator/styles/index.d.ts.map +1 -1
  107. package/types/components/spinner/components/spinner.d.ts +14 -3
  108. package/types/components/spinner/components/spinner.d.ts.map +1 -1
  109. package/types/components/spinner/styles/index.d.ts +3 -2
  110. package/types/components/spinner/styles/index.d.ts.map +1 -1
  111. package/types/components/splitview/components/splitview.d.ts +22 -4
  112. package/types/components/splitview/components/splitview.d.ts.map +1 -1
  113. package/types/components/splitview/components/splitview.detail.d.ts +12 -2
  114. package/types/components/splitview/components/splitview.detail.d.ts.map +1 -1
  115. package/types/components/splitview/components/splitview.master.d.ts +12 -1
  116. package/types/components/splitview/components/splitview.master.d.ts.map +1 -1
  117. package/types/components/translate/components/translate.d.ts +44 -10
  118. package/types/components/translate/components/translate.d.ts.map +1 -1
@@ -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
@@ -1,20 +1,43 @@
1
1
  // @ts-nocheck
2
2
 
3
+ const stylesheetRegistry = new Map()
4
+ const fallbackRegistry = new Map()
5
+
3
6
  /** @param {string} tag
4
7
  * @param {CustomElementConstructor} element
5
- * @param {string} styles **/
8
+ * @param {string} [styles]
9
+ * @returns {CSSStyleSheet|HTMLStyleElement|undefined} */
6
10
  export function define (tag, element, styles = '') {
7
- globalThis.customElements.define(tag, element)
11
+ const definedElement = globalThis.customElements.get(tag)
12
+ if (!definedElement) {
13
+ globalThis.customElements.define(tag, element)
14
+ }
8
15
  if (!styles?.trim()) return
16
+ if (typeof document === 'undefined') return
9
17
 
10
18
  try {
11
- const sheet = new globalThis.CSSStyleSheet()
19
+ let sheet = stylesheetRegistry.get(tag)
20
+ if (!sheet) {
21
+ sheet = new globalThis.CSSStyleSheet()
22
+ stylesheetRegistry.set(tag, sheet)
23
+ }
12
24
  sheet.replaceSync(styles)
13
- return (document.adoptedStyleSheets = [
14
- ...document.adoptedStyleSheets, sheet])
25
+
26
+ const adopted = document.adoptedStyleSheets || []
27
+ if (!adopted.includes(sheet)) {
28
+ document.adoptedStyleSheets = [...adopted, sheet]
29
+ }
30
+
31
+ return sheet
15
32
  } catch (error) {
16
- const style = document.createElement('style')
33
+ let style = fallbackRegistry.get(tag)
34
+ if (!style) {
35
+ style = document.createElement('style')
36
+ style.setAttribute('data-componark-tag', tag)
37
+ fallbackRegistry.set(tag, style)
38
+ document.head.appendChild(style)
39
+ }
17
40
  style.textContent = styles
18
- document.head.appendChild(style)
41
+ return style
19
42
  }
20
43
  }
@@ -1,62 +1,149 @@
1
- import { jest } from '@jest/globals'
1
+ import { it, mock } from 'node:test'
2
+ import assert from 'node:assert/strict'
2
3
  import { define } from './define.js'
3
4
 
4
- describe('Define', () => {
5
- it('can define a custome element', () => {
6
- class NewElement extends globalThis.HTMLElement {}
7
- define('new-element', NewElement)
5
+ it('can define a custome element', () => {
6
+ class NewElement extends globalThis.HTMLElement {}
7
+ define('new-element', NewElement)
8
8
 
9
- const newElement = document.createElement('new-element')
10
- expect(newElement).toBeTruthy()
11
- })
9
+ const newElement = document.createElement('new-element')
10
+ assert.ok(newElement)
11
+ })
12
12
 
13
- it('can define a custom element using constructable CSSStyleSheet', () => {
14
- let definedStyles = null
15
- class MockCSSStyleSheet extends globalThis.CSSStyleSheet {
16
- replaceSync (styles) {
17
- definedStyles = styles
18
- }
13
+ it('can define a custom element using constructable CSSStyleSheet', () => {
14
+ let definedStyles = null
15
+ class MockCSSStyleSheet extends globalThis.CSSStyleSheet {
16
+ replaceSync (styles) {
17
+ definedStyles = styles
19
18
  }
19
+ }
20
20
 
21
- jest.spyOn(window, 'CSSStyleSheet').mockReturnValue(
22
- new MockCSSStyleSheet())
23
-
24
- class CSSStyledElement extends globalThis.HTMLElement {}
25
- const styles = `
26
- body {
27
- color: red;
21
+ const cssStyleSheetSpy = mock.method(
22
+ globalThis,
23
+ 'CSSStyleSheet',
24
+ function () {
25
+ return new MockCSSStyleSheet()
28
26
  }
27
+ )
28
+
29
+ class CSSStyledElement extends globalThis.HTMLElement {}
30
+ const styles = `
31
+ body {
32
+ color: red;
33
+ }
34
+
35
+ div {
36
+ margin: 5px;
37
+ }
38
+ `
39
+
40
+ define('css-styled-element', CSSStyledElement, styles)
29
41
 
30
- div {
31
- margin: 5px;
42
+ const styledElement = document.createElement('css-styled-element')
43
+ assert.ok(styledElement)
44
+
45
+ assert.deepStrictEqual(styles, definedStyles)
46
+ cssStyleSheetSpy.mock.restore()
47
+ })
48
+
49
+ it('can define a custom element using an style fallback element', () => {
50
+ const cssStyleSheetSpy = mock.method(
51
+ globalThis,
52
+ 'CSSStyleSheet',
53
+ function () {
54
+ throw new Error('Constructable stylesheets are unavailable')
32
55
  }
33
- `
56
+ )
57
+
58
+ class StyledElement extends globalThis.HTMLElement {}
59
+ const styles = `
60
+ body {
61
+ color: red;
62
+ }
63
+
64
+ div {
65
+ margin: 5px;
66
+ }
67
+ `
68
+
69
+ define('styled-element', StyledElement, styles)
70
+ cssStyleSheetSpy.mock.restore()
34
71
 
35
- define('css-styled-element', CSSStyledElement, styles)
72
+ const styledElement = document.createElement('styled-element')
73
+ assert.ok(styledElement)
74
+ const style = Array.from(document.head.querySelectorAll('style')).find(
75
+ (item) => item.textContent.includes('color: red;')
76
+ )
77
+ assert.ok(style)
78
+ })
36
79
 
37
- const styledElement = document.createElement('css-styled-element')
38
- expect(styledElement).toBeTruthy()
80
+ it('does not throw when defining an already defined custom element', () => {
81
+ class DuplicateElement extends globalThis.HTMLElement {}
82
+ define('duplicate-element', DuplicateElement)
39
83
 
40
- expect(styles).toEqual(definedStyles)
84
+ assert.doesNotThrow(() => {
85
+ define('duplicate-element', DuplicateElement)
41
86
  })
87
+ })
42
88
 
43
- it('can define a custom element using an style fallback element', () => {
44
- class StyledElement extends globalThis.HTMLElement {}
45
- const styles = `
46
- body {
47
- color: red;
89
+ it('reuses fallback styles for repeated definitions of the same tag', () => {
90
+ const cssStyleSheetSpy = mock.method(
91
+ globalThis,
92
+ 'CSSStyleSheet',
93
+ function () {
94
+ throw new Error('Constructable stylesheets are unavailable')
48
95
  }
96
+ )
97
+
98
+ class ReusedStyledElement extends globalThis.HTMLElement {}
99
+
100
+ define('reused-styled-element', ReusedStyledElement, 'body { color: red; }')
101
+ define('reused-styled-element', ReusedStyledElement, 'body { color: blue; }')
102
+
103
+ cssStyleSheetSpy.mock.restore()
104
+
105
+ const styles = Array.from(
106
+ document.head.querySelectorAll('style[data-componark-tag="reused-styled-element"]')
107
+ )
108
+ assert.deepStrictEqual(styles.length, 1)
109
+ assert.ok(styles[0].textContent.includes('color: blue;'))
110
+ })
49
111
 
50
- div {
51
- margin: 5px;
112
+ it('does not add a constructable stylesheet more than once for the same tag', () => {
113
+ class MockCSSStyleSheet extends globalThis.CSSStyleSheet {
114
+ replaceSync (_styles) {}
115
+ }
116
+ const cssStyleSheetSpy = mock.method(
117
+ globalThis,
118
+ 'CSSStyleSheet',
119
+ function () {
120
+ return new MockCSSStyleSheet()
52
121
  }
53
- `
122
+ )
123
+ class OnceStyledElement extends globalThis.HTMLElement {}
54
124
 
55
- define('styled-element', StyledElement, styles)
125
+ const beforeCount = document.adoptedStyleSheets.length
126
+ define('once-styled-element', OnceStyledElement, 'body { color: orange; }')
127
+ const firstCount = document.adoptedStyleSheets.length
128
+ define('once-styled-element', OnceStyledElement, 'body { color: green; }')
129
+ const secondCount = document.adoptedStyleSheets.length
56
130
 
57
- const styledElement = document.createElement('styled-element')
58
- expect(styledElement).toBeTruthy()
59
- const style = document.head.querySelector('style')
60
- expect(style).toBeTruthy()
61
- })
131
+ cssStyleSheetSpy.mock.restore()
132
+ assert.deepStrictEqual(firstCount, beforeCount + 1)
133
+ assert.deepStrictEqual(secondCount, firstCount)
134
+ })
135
+
136
+ it('returns early when styles are provided but document is unavailable', () => {
137
+ const previousDocument = globalThis.document
138
+ class NoDocumentElement extends globalThis.HTMLElement {}
139
+
140
+ delete globalThis.document
141
+
142
+ try {
143
+ assert.doesNotThrow(() => {
144
+ define('no-document-element', NoDocumentElement, 'body { color: red; }')
145
+ })
146
+ } finally {
147
+ globalThis.document = previousDocument
148
+ }
62
149
  })
@@ -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,19 +1,19 @@
1
+ import { it } from 'node:test';
2
+ import assert from 'node:assert/strict';
1
3
  import { camelToKebab, kebabToCamel, snakeToCamel } from './format.js'
2
4
 
3
- describe('Format', () => {
4
- it('can convert camel to kebab', () => {
5
- const camel = 'myVariable'
6
- const kebab = camelToKebab(camel)
7
- expect(kebab).toBe('my-variable')
8
- })
9
- it('can convert kebab to camel', () => {
10
- const kebab = 'my-variable'
11
- const camel = kebabToCamel(kebab)
12
- expect(camel).toBe('myVariable')
13
- })
14
- it('can convert snake to camel', () => {
15
- const kebab = 'my_variable'
16
- const camel = snakeToCamel(kebab)
17
- expect(camel).toBe('myVariable')
18
- })
5
+ it('can convert camel to kebab', () => {
6
+ const camel = 'myVariable'
7
+ const kebab = camelToKebab(camel)
8
+ assert.strictEqual(kebab, 'my-variable')
9
+ })
10
+ it('can convert kebab to camel', () => {
11
+ const kebab = 'my-variable'
12
+ const camel = kebabToCamel(kebab)
13
+ assert.strictEqual(camel, 'myVariable')
14
+ })
15
+ it('can convert snake to camel', () => {
16
+ const kebab = 'my_variable'
17
+ const camel = snakeToCamel(kebab)
18
+ assert.strictEqual(camel, 'myVariable')
19
19
  })
@@ -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
@@ -25,8 +32,11 @@ export function listen (self) {
25
32
  if (pipe?.toLocaleLowerCase() === 'object') {
26
33
  return set(this, objectPath.trim(), event.detail)
27
34
  }
28
- set(this, objectPath.trim(), transform(pipe, get(
29
- event, eventPath, get(event, 'detail', ''))))
35
+ const inputValue = (
36
+ get(event, eventPath, undefined) ||
37
+ get(event, 'detail', '')
38
+ )
39
+ set(this, objectPath.trim(), transform(pipe, inputValue))
30
40
  }
31
41
  }
32
42
 
@@ -59,7 +69,11 @@ export function listen (self) {
59
69
  }
60
70
  }
61
71
 
62
- /** @param {HTMLElement} self */
72
+ /**
73
+ * Resolve dependencies from descendants using `resolve` events.
74
+ * @param {Component|HTMLElement} self
75
+ * @returns {void}
76
+ */
63
77
  function provide (self) {
64
78
  /** @ts-ignore */
65
79
  if (!self.provide) return
@@ -99,9 +113,16 @@ function transform (pipe, value) {
99
113
  }[pipe?.toLowerCase() || 'string'](value)
100
114
  }
101
115
 
102
- /** @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
+ */
103
123
  export function set (object, path, value) {
104
124
  const pathArray = path.match(/([^[.\]])+/g)
125
+ if (!pathArray?.length) return
105
126
 
106
127
  pathArray.reduce((accumulator, key, index) => {
107
128
  if (accumulator[key.trim()] === undefined) accumulator[key.trim()] = {}
@@ -110,15 +131,27 @@ export function set (object, path, value) {
110
131
  }, object)
111
132
  }
112
133
 
113
- /** @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
+ */
114
141
  export function get (object, path, fallback) {
115
142
  const pathArray = path.match(/([^[.\]])+/g)
143
+ if (!pathArray?.length) return fallback
116
144
 
117
- return pathArray.reduce((accumulator, key) => accumulator &&
118
- accumulator[key.trim()], object) || fallback
145
+ const value = pathArray.reduce((accumulator, key) => accumulator &&
146
+ accumulator[key.trim()], object)
147
+
148
+ return value === undefined ? fallback : value
119
149
  }
120
150
 
121
- /** @param {object} object @return {string} */
151
+ /** @param {object} object
152
+ * @return {string}
153
+ * @description Returns truthy CSS class names from object values.
154
+ */
122
155
  export function keys (object) {
123
156
  return Object.keys(object).filter(
124
157
  key => Boolean(object[key])).join(' ')