@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.
Files changed (116) hide show
  1. package/Makefile +49 -0
  2. package/README.md +47 -0
  3. package/knowarkjs.code-workspace +29 -0
  4. package/lib/base/component/README.rst +113 -0
  5. package/lib/base/component/component.js +115 -0
  6. package/lib/base/component/component.test.js +327 -0
  7. package/lib/base/component/index.js +3 -0
  8. package/lib/base/index.js +1 -0
  9. package/lib/base/styles/index.js +3 -0
  10. package/lib/base/styles/styles.js +320 -0
  11. package/lib/base/utils/define.js +21 -0
  12. package/lib/base/utils/define.test.js +62 -0
  13. package/lib/base/utils/format.js +24 -0
  14. package/lib/base/utils/format.test.js +19 -0
  15. package/lib/base/utils/helpers.js +96 -0
  16. package/lib/base/utils/helpers.test.js +154 -0
  17. package/lib/base/utils/index.js +5 -0
  18. package/lib/base/utils/slots.js +18 -0
  19. package/lib/base/utils/slots.test.js +52 -0
  20. package/lib/base/utils/uuid.js +9 -0
  21. package/lib/base/utils/uuid.test.js +19 -0
  22. package/lib/components/audio/README.md +22 -0
  23. package/lib/components/audio/components/audio.js +103 -0
  24. package/lib/components/audio/components/audio.test.js +127 -0
  25. package/lib/components/audio/index.js +1 -0
  26. package/lib/components/audio/styles/ark.css.js +83 -0
  27. package/lib/components/audio/styles/index.js +2 -0
  28. package/lib/components/camera/README.md +64 -0
  29. package/lib/components/camera/components/camera.js +85 -0
  30. package/lib/components/camera/components/camera.test.js +104 -0
  31. package/lib/components/camera/index.js +1 -0
  32. package/lib/components/camera/styles/ark.css.js +17 -0
  33. package/lib/components/camera/styles/index.js +2 -0
  34. package/lib/components/capture/components/capture.js +54 -0
  35. package/lib/components/capture/components/capture.test.js +112 -0
  36. package/lib/components/capture/index.js +1 -0
  37. package/lib/components/droparea/README.md +51 -0
  38. package/lib/components/droparea/components/droparea-preview.js +159 -0
  39. package/lib/components/droparea/components/droparea-preview.test.js +105 -0
  40. package/lib/components/droparea/components/droparea.js +165 -0
  41. package/lib/components/droparea/components/droparea.test.js +320 -0
  42. package/lib/components/droparea/index.js +1 -0
  43. package/lib/components/droparea/styles/ark.css.js +235 -0
  44. package/lib/components/droparea/styles/index.js +3 -0
  45. package/lib/components/emit/components/emit.js +33 -0
  46. package/lib/components/emit/components/emit.test.js +138 -0
  47. package/lib/components/emit/index.js +1 -0
  48. package/lib/components/index.js +9 -0
  49. package/lib/components/list/README.md +103 -0
  50. package/lib/components/list/components/item.test.js +93 -0
  51. package/lib/components/list/components/list.item.js +22 -0
  52. package/lib/components/list/components/list.js +96 -0
  53. package/lib/components/list/components/list.test.js +267 -0
  54. package/lib/components/list/index.js +2 -0
  55. package/lib/components/paginator/README.md +32 -0
  56. package/lib/components/paginator/components/paginator.js +110 -0
  57. package/lib/components/paginator/components/paginator.test.js +131 -0
  58. package/lib/components/paginator/index.js +1 -0
  59. package/lib/components/paginator/styles/ark.css.js +196 -0
  60. package/lib/components/paginator/styles/index.js +2 -0
  61. package/lib/components/spinner/README.md +41 -0
  62. package/lib/components/spinner/components/spinner.js +105 -0
  63. package/lib/components/spinner/components/spinner.test.js +50 -0
  64. package/lib/components/spinner/index.js +1 -0
  65. package/lib/components/spinner/styles/ark.css.js +658 -0
  66. package/lib/components/spinner/styles/index.js +2 -0
  67. package/lib/components/splitview/README.md +63 -0
  68. package/lib/components/splitview/components/splitview.detail.js +46 -0
  69. package/lib/components/splitview/components/splitview.detail.test.js +92 -0
  70. package/lib/components/splitview/components/splitview.js +69 -0
  71. package/lib/components/splitview/components/splitview.master.js +26 -0
  72. package/lib/components/splitview/components/splitview.master.test.js +55 -0
  73. package/lib/components/splitview/components/splitview.test.js +76 -0
  74. package/lib/components/splitview/index.js +3 -0
  75. package/lib/components/translate/README.md +56 -0
  76. package/lib/components/translate/components/translate.js +100 -0
  77. package/lib/components/translate/components/translate.test.js +226 -0
  78. package/lib/components/translate/index.js +1 -0
  79. package/lib/index.js +2 -0
  80. package/package.json +68 -0
  81. package/showcase/design/.htaccess +8 -0
  82. package/showcase/design/core/factories/development/development.factory.js +5 -0
  83. package/showcase/design/core/factories/development/index.js +1 -0
  84. package/showcase/design/core/factories/index.js +11 -0
  85. package/showcase/design/core/factories/standard.factory.js +19 -0
  86. package/showcase/design/index.html +22 -0
  87. package/showcase/design/index.js +7 -0
  88. package/showcase/design/screens/base/audio/audioDemo.js +32 -0
  89. package/showcase/design/screens/base/audio/index.js +25 -0
  90. package/showcase/design/screens/base/camera/cameraDemo.js +83 -0
  91. package/showcase/design/screens/base/camera/index.js +25 -0
  92. package/showcase/design/screens/base/droparea/dropareaDemo.js +88 -0
  93. package/showcase/design/screens/base/droparea/index.js +25 -0
  94. package/showcase/design/screens/base/index.js +42 -0
  95. package/showcase/design/screens/base/list/index.js +25 -0
  96. package/showcase/design/screens/base/list/listDemo.js +89 -0
  97. package/showcase/design/screens/base/paginator/index.js +25 -0
  98. package/showcase/design/screens/base/paginator/paginatorDemo.js +82 -0
  99. package/showcase/design/screens/base/root.component.js +294 -0
  100. package/showcase/design/screens/base/root.routes.js +28 -0
  101. package/showcase/design/screens/base/spinner/index.js +25 -0
  102. package/showcase/design/screens/base/spinner/spinnerDemo.js +55 -0
  103. package/showcase/design/screens/base/splitview/detailDemo.js +40 -0
  104. package/showcase/design/screens/base/splitview/index.js +25 -0
  105. package/showcase/design/screens/base/splitview/splitViewDemo.js +58 -0
  106. package/showcase/design/screens/base/translate/index.js +20 -0
  107. package/showcase/design/screens/base/translate/translateDemo.js +43 -0
  108. package/showcase/design/screens/main.js +12 -0
  109. package/showcase/design/screens/screens.routes.js +23 -0
  110. package/showcase/index.html +86 -0
  111. package/showcase/index.js +5 -0
  112. package/showcase/locales/en/default.json +5 -0
  113. package/showcase/locales/es/default.json +5 -0
  114. package/showcase/locales/fr/default.json +5 -0
  115. package/tsconfig.json +23 -0
  116. package/webpack.config.cjs +118 -0
@@ -0,0 +1,320 @@
1
+ const userActions = ['-hover', '-active' ]
2
+
3
+ const formActions = ['-focus-within', '-valid', '-invalid', '-checked']
4
+
5
+ const actions = ['', ...userActions, ...formActions]
6
+
7
+ const sides = ['', '-top', '-right', '-bottom', '-left']
8
+
9
+ const mutableProperties = [
10
+ 'display', 'background', 'color', 'outline',
11
+ 'border-style', 'border-width', 'border-color', 'border-radius']
12
+
13
+ const effects = [
14
+ 'animation', 'filter', 'mix-blend-mode', 'transition', 'transform']
15
+
16
+ const css = String.raw; export default css`
17
+ * {
18
+ margin: 0;
19
+ padding: 0;
20
+ box-sizing: border-box;
21
+ }
22
+
23
+ html {
24
+ /* COLORS (RGB) */
25
+
26
+ --primary-rgb: 0, 0, 255; /* blue */
27
+ --secondary-rgb: 255, 165, 0; /* orange */
28
+ --tertiary-rgb: 238, 130, 238; /* violet */
29
+ --success-rgb: 0, 255, 0; /* green */
30
+ --danger-rgb: 255, 0, 0; /* red */
31
+ --warning-rgb: 255, 255, 0; /* yellow */
32
+ --info-rgb: 0, 255, 255; /* cyan */
33
+ --dark-rgb: 0, 0, 0; /* black */
34
+ --muted-rgb: 128, 128, 128; /* gray */
35
+ --light-rgb: 211, 211, 211; /*lightgray */
36
+
37
+ --primary: rgb(var(--primary-rgb));
38
+ --secondary: rgb(var(--secondary-rgb));
39
+ --success: rgb(var(--success-rgb));
40
+ --danger: rgb(var(--danger-rgb));
41
+ --warning: rgb(var(--warning-rgb));
42
+ --info: rgb(var(--info-rgb));
43
+ --dark: rgb(var(--dark-rgb));
44
+ --muted: rgb(var(--muted-rgb));
45
+ --light: rgb(var(--light-rgb));
46
+
47
+ /* TYPOGRAPHY */
48
+
49
+ --font-size-step: 0.25rem;
50
+ --line-height-step: 0.5;
51
+ --word-spacing-step: 0.1rem;
52
+ --letter-spacing-step: 0.05rem;
53
+
54
+ /* SPACING */
55
+
56
+ --margin-step: 4px;
57
+ --padding-step: 4px;
58
+
59
+ /* LAYOUT */
60
+
61
+ --row-step: 80px;
62
+ --column-step: 240px;
63
+ --gap-step: 4px;
64
+ --width-step: 12px;
65
+ --height-step: 12px;
66
+ --position-step: 12px;
67
+
68
+ /* BORDERS */
69
+
70
+ --border-width-step: 1px;
71
+ --outline-width-step: 1px;
72
+ --border-radius-step: 1px;
73
+
74
+ /* SHADOWS */
75
+
76
+ --box-shadow-offset-step: 1px;
77
+ --box-shadow-blur-step: 2px;
78
+ --box-shadow-spread-step: -0.25px;
79
+ --box-shadow-color: rgb(var(--dark-rgb), 0.5);
80
+ }
81
+
82
+ [style*='--all:'] {
83
+ all: var(--all);
84
+ margin: 0;
85
+ padding: 0;
86
+ box-sizing: border-box;
87
+ }
88
+
89
+ /* COLORS */
90
+
91
+ ${actions.map(action => css`
92
+ [style*='--color${action}:']${action.replace('-', ':')} {
93
+ color: var(--color${action});
94
+ }
95
+ `.trim()).join('\n')}
96
+ ${actions.map(action => css`
97
+ [style*='--background${action}:']${action.replace('-', ':')} {
98
+ background: var(--background${action});
99
+ }
100
+ `.trim()).join('\n')}
101
+
102
+ /* TYPOGRAPHY */
103
+
104
+ [style*='--font-size:'] {
105
+ font-size: calc(var(--font-size-step) * var(--font-size));
106
+ }
107
+ [style*='--line-height:'] {
108
+ line-height: calc(var(--line-height-step) * var(--line-height));
109
+ }
110
+ [style*='--word-spacing:'] {
111
+ word-spacing: calc(var(--word-spacing-step) * var(--word-spacing));
112
+ }
113
+ [style*='--letter-spacing:'] {
114
+ letter-spacing: calc(var(--letter-spacing-step) * var(--letter-spacing));
115
+ }
116
+
117
+ /* SPACING */
118
+
119
+ ${sides.map(side => css`
120
+ [style*='--margin${side}:'] {
121
+ margin${side}: calc(var(--margin-step) * var(--margin${side}));
122
+ }
123
+ `.trim()).join('\n')}
124
+ [style*='--margin-horizontal:'] {
125
+ margin-left: calc(var(--margin-step) * var(--margin-horizontal));
126
+ margin-right: calc(var(--margin-step) * var(--margin-horizontal));
127
+ }
128
+ [style*='--margin-vertical:'] {
129
+ margin-top: calc(var(--margin-step) * var(--margin-vertical));
130
+ margin-bottom: calc(var(--margin-step) * var(--margin-vertical));
131
+ }
132
+
133
+ ${sides.map(side => css`
134
+ [style*='--padding${side}:'] {
135
+ padding${side}: calc(var(--padding-step) * var(--padding${side}));
136
+ }
137
+ `.trim()).join('\n')}
138
+ [style*='--padding-horizontal:'] {
139
+ padding-left: calc(var(--padding-step) * var(--padding-horizontal));
140
+ padding-right: calc(var(--padding-step) * var(--padding-horizontal));
141
+ }
142
+ [style*='--padding-vertical:'] {
143
+ padding-top: calc(var(--padding-step) * var(--padding-vertical));
144
+ padding-bottom: calc(var(--padding-step) * var(--padding-vertical));
145
+ }
146
+
147
+ /* LAYOUT */
148
+
149
+ [style*='--grid:'] {
150
+ display: grid;
151
+ grid: var(--grid);
152
+ }
153
+ [style*='--flow:'] {
154
+ display: grid;
155
+ grid-auto-flow: var(--flow);
156
+ place-items: center;
157
+ }
158
+ [style*='--row-span:'] {
159
+ grid-row: span var(--row-span);
160
+ }
161
+ [style*='--column-span:'] {
162
+ grid-column: span var(--column-span);
163
+ }
164
+ [style*='--rows:'] {
165
+ display: grid;
166
+ grid-template-rows: repeat(auto-fit, minmax(
167
+ min(100%, max(var(--row-step), 100%/var(--rows))), 1fr));
168
+ }
169
+ [style*='--columns:'] {
170
+ display: grid;
171
+ grid-template-columns: repeat(auto-fit, minmax(
172
+ min(100%, max(var(--column-step), 100%/var(--columns))), 1fr));
173
+ }
174
+ [style*='--gap:'] {
175
+ gap: calc(var(--gap-step) * var(--gap));
176
+ }
177
+ [style*='--display:'] {
178
+ display: var(--display);
179
+ }
180
+
181
+ [style*='--width:'] {
182
+ width: calc(var(--width-step) * var(--width));
183
+ }
184
+ [style*='--max-width:'] {
185
+ max-width: calc(var(--width-step) * var(--max-width));
186
+ }
187
+ [style*='--min-width:'] {
188
+ min-width: calc(var(--width-step) * var(--min-width));
189
+ }
190
+
191
+ [style*='--height:'] {
192
+ height: calc(var(--height-step) * var(--height));
193
+ }
194
+ [style*='--max-height:'] {
195
+ max-height: calc(var(--height-step) * var(--max-height));
196
+ }
197
+ [style*='--min-height:'] {
198
+ min-height: calc(var(--height-step) * var(--min-height));
199
+ }
200
+
201
+ [style*='--inset:'] {
202
+ inset: calc(var(--position-step) * var(--inset));
203
+ }
204
+ ${sides.filter(Boolean).map(side => css`
205
+ [style*='-${side}:'] {
206
+ ${side.replace('-', '')}: calc(var(--position-step) * var(-${side}));
207
+ }
208
+ `.trim()).join('\n')}
209
+
210
+ /* BORDERS */
211
+
212
+ ${actions.map(action => css`
213
+ [style*='--border-style${action}:']${action.replace('-', ':')} {
214
+ border-style: var(--border-style${action});
215
+ }
216
+ [style*='--border-width${action}:']${action.replace('-', ':')} {
217
+ border-width: var(--border-width${action});
218
+ }
219
+ [style*='--border-color${action}:']${action.replace('-', ':')} {
220
+ border-color: var(--border-color${action});
221
+ }
222
+ [style*='--border-radius${action}:']${action.replace('-', ':')} {
223
+ border-radius: var(--border-radius${action});
224
+ }
225
+ `.trim()).join('\n')}
226
+
227
+ ${actions.map(action => css`
228
+ [style*='--outline${action}:']${action.replace('-', ':')} {
229
+ outline: var(--outline${action});
230
+ }
231
+ `.trim()).join('\n')}
232
+
233
+ /* SHADOWS */
234
+
235
+ ${actions.map(action => css`
236
+ [style*='--box-shadow${action}:']${action.replace('-', ':')} {
237
+ box-shadow: 0
238
+ calc(var(--box-shadow-offset-step) * var(--box-shadow${action}))
239
+ calc(var(--box-shadow-blur-step) * var(--box-shadow${action}))
240
+ calc(var(--box-shadow-spread-step) * var(--box-shadow${action}))
241
+ var(--box-shadow-color);
242
+ }
243
+ `.trim()).join('\n')}
244
+
245
+ /* EFFECTS */
246
+
247
+ ${effects.map(effect => actions.map(action => css`
248
+ [style*='--${effect}${action}:']${action.replace('-', ':')} {
249
+ ${effect}: var(--${effect}${action});
250
+ }
251
+ `.trim())).flat().join('\n')}
252
+
253
+ /* COMBINATORS */
254
+
255
+ ${[...mutableProperties, ...effects].map(
256
+ effect => userActions.map(action => css`
257
+ *${action.replace('-', ':')} > [style*='--${effect}-parent${action}:'] {
258
+ ${effect}: var(--${effect}-parent${action});
259
+ }
260
+ `.trim())).flat().join('\n')}
261
+
262
+ ${[...mutableProperties, ...effects].map(
263
+ effect => formActions.map(action => css`
264
+ *${action.replace('-', ':')} ~ [style*='--${effect}-antecessor${action}:'] {
265
+ ${effect}: var(--${effect}-antecessor${action});
266
+ }
267
+ `.trim())).flat().join('\n')}
268
+
269
+ /* MEDIA QUERIES */
270
+
271
+ @media (min-width: 960px) {
272
+ [style*='--grid-large:'] {
273
+ display: grid;
274
+ grid: var(--grid-large);
275
+ }
276
+
277
+ [style*='--display-large:'] {
278
+ display: var(--display-large);
279
+ }
280
+ }
281
+
282
+ /* ANIMATIONS */
283
+
284
+ @keyframes spin {
285
+ from {
286
+ transform: rotate(0deg);
287
+ }
288
+ to {
289
+ transform: rotate(360deg);
290
+ }
291
+ }
292
+
293
+ @keyframes ping {
294
+ 75%, 100% {
295
+ transform: scale(2);
296
+ opacity: 0;
297
+ }
298
+ }
299
+
300
+ @keyframes pulse {
301
+ 0%, 100% {
302
+ opacity: 1;
303
+ }
304
+ 50% {
305
+ opacity: .5;
306
+ }
307
+ }
308
+
309
+ @keyframes bounce {
310
+ 0%, 100% {
311
+ transform: translateY(-25%);
312
+ animation-timing-function: cubic-bezier(0.8, 0, 1, 1);
313
+ }
314
+ 50% {
315
+ transform: translateY(0);
316
+ animation-timing-function: cubic-bezier(0, 0, 0.2, 1);
317
+ }
318
+ }
319
+
320
+ `.trim()
@@ -0,0 +1,21 @@
1
+ // @ts-nocheck
2
+
3
+ /** @param {string} tag
4
+ * @param {CustomElementConstructor} element
5
+ * @param {string} styles **/
6
+ export function define(tag, element, styles='') {
7
+ customElements.define(tag, element)
8
+ if (!styles?.trim()) return
9
+
10
+ try {
11
+ const sheet = new CSSStyleSheet()
12
+ sheet.replaceSync(styles)
13
+ return document.adoptedStyleSheets = [
14
+ ...document.adoptedStyleSheets, sheet]
15
+ } catch (error) {
16
+ const style = document.createElement('style')
17
+ style.textContent = styles
18
+ document.head.appendChild(style);
19
+ }
20
+
21
+ }
@@ -0,0 +1,62 @@
1
+ import { jest } from '@jest/globals'
2
+ import { define } from './define.js'
3
+
4
+ describe('Define', () => {
5
+ it('can define a custome element', () => {
6
+ class NewElement extends HTMLElement {}
7
+ define('new-element', NewElement)
8
+
9
+ const newElement = document.createElement('new-element')
10
+ expect(newElement).toBeTruthy()
11
+ })
12
+
13
+ it('can define a custom element using constructable CSSStyleSheet', () => {
14
+ let definedStyles = null
15
+ class MockCSSStyleSheet extends CSSStyleSheet {
16
+ replaceSync (styles) {
17
+ definedStyles = styles
18
+ }
19
+ }
20
+
21
+ jest.spyOn(window, "CSSStyleSheet").mockReturnValue(
22
+ new MockCSSStyleSheet())
23
+
24
+ class CSSStyledElement extends HTMLElement {}
25
+ const styles = `
26
+ body {
27
+ color: red;
28
+ }
29
+
30
+ div {
31
+ margin: 5px;
32
+ }
33
+ `
34
+
35
+ define('css-styled-element', CSSStyledElement, styles)
36
+
37
+ const styledElement = document.createElement('css-styled-element')
38
+ expect(styledElement).toBeTruthy()
39
+
40
+ expect(styles).toEqual(definedStyles)
41
+ })
42
+
43
+ it('can define a custom element using an style fallback element', () => {
44
+ class StyledElement extends HTMLElement {}
45
+ const styles = `
46
+ body {
47
+ color: red;
48
+ }
49
+
50
+ div {
51
+ margin: 5px;
52
+ }
53
+ `
54
+
55
+ define('styled-element', StyledElement, styles)
56
+
57
+ const styledElement = document.createElement('styled-element')
58
+ expect(styledElement).toBeTruthy()
59
+ const style = document.head.querySelector('style')
60
+ expect(style).toBeTruthy()
61
+ })
62
+ })
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Convert Strings from camelCase to kebab-case
3
+ * @param {string} input @returns {string} */
4
+ export function camelToKebab (input) {
5
+ return input.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase()
6
+ }
7
+
8
+ /**
9
+ * Convert Strings from kebab-case to camelCase
10
+ * @param {string} input @returns {string} */
11
+ export function kebabToCamel (input) {
12
+ return input.replace(/-([a-z])/g, function (g) {
13
+ return g[1].toUpperCase()
14
+ })
15
+ }
16
+
17
+ /**
18
+ * Convert Strings from snake to camelCase
19
+ * @param {string} input @returns {string} */
20
+ export function snakeToCamel (input) {
21
+ return input.replace(/_([a-z])/g, function (g) {
22
+ return g[1].toUpperCase()
23
+ })
24
+ }
@@ -0,0 +1,19 @@
1
+ import { camelToKebab, kebabToCamel, snakeToCamel } from './format.js'
2
+
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
+ })
19
+ })
@@ -0,0 +1,96 @@
1
+ import { camelToKebab } from './format.js'
2
+
3
+ /** @param {HTMLElement} self */
4
+ export function listen (self) {
5
+ const binding = self['binding']
6
+ const elements = self.querySelectorAll(`[${binding}]`)
7
+ for (const element of elements) {
8
+ for (const attribute of Array.from(element.attributes)) {
9
+ if (attribute.name.startsWith('on-')) {
10
+ const eventName = attribute.name.replace('on-', '').trim()
11
+ let handler = self[attribute.value]
12
+
13
+ const [assignment] = attribute.value.match(/[^{{]+(?=\}})/g) || []
14
+
15
+ if (assignment) {
16
+ let [objectPath, eventPath] = assignment.split('=')
17
+ eventPath = eventPath || 'target.value'
18
+ handler = function (event) {
19
+ set(this, objectPath.trim(), get(
20
+ event, eventPath, get(event, 'detail', '')))
21
+ }
22
+ }
23
+
24
+ if (!handler) continue
25
+
26
+ const catchingHandler = async function (event) {
27
+ try {
28
+ return await Promise.resolve(handler.bind(this)(event))
29
+ } catch (error) {
30
+ this.dispatchEvent(new CustomEvent('error', {
31
+ detail: error, bubbles: true, cancelable: true }))
32
+ }
33
+ }
34
+
35
+ element.addEventListener(eventName, catchingHandler.bind(self))
36
+ element.removeAttribute(binding)
37
+ }
38
+ }
39
+ }
40
+ }
41
+
42
+ /** @param {HTMLElement} self */
43
+ function provide (self) {
44
+ if (!self.provide) return
45
+ self.addEventListener('resolve', (event) => {
46
+ const resource = event.detail.resource
47
+ const dependency = self.provide(resource)
48
+ if (dependency === undefined) return
49
+ event.detail[resource] = dependency
50
+ })
51
+
52
+ }
53
+
54
+ /** @param {HTMLElement} self @param {string[]} properties */
55
+ export function reflect (self, properties) {
56
+ /** @type {PropertyDescriptorMap} */
57
+ const descriptors = {}
58
+ for (const property of properties) {
59
+ const attribute = camelToKebab(property)
60
+ descriptors[property] = {
61
+ get: () => self.getAttribute(attribute) || '',
62
+ set: value => {
63
+ if (value !== undefined) {
64
+ self.setAttribute(attribute, value)
65
+ }
66
+ }
67
+ }
68
+ }
69
+ Object.defineProperties(self, descriptors)
70
+ provide(self)
71
+ }
72
+
73
+ /** @param {object} object @param {string} path @param {any} value */
74
+ export function set (object, path, value) {
75
+ const pathArray = path.match(/([^[.\]])+/g)
76
+
77
+ pathArray.reduce((accumulator, key, index) => {
78
+ if (accumulator[key.trim()] === undefined) accumulator[key.trim()] = {}
79
+ if (index === pathArray.length - 1) accumulator[key.trim()] = value
80
+ return accumulator[key.trim()]
81
+ }, object)
82
+ }
83
+
84
+ /** @param {object} object @param {string} path @param {any} fallback */
85
+ export function get (object, path, fallback) {
86
+ const pathArray = path.match(/([^[.\]])+/g)
87
+
88
+ return pathArray.reduce((accumulator, key) => accumulator &&
89
+ accumulator[key.trim()], object) || fallback
90
+ }
91
+
92
+ /** @param {object} object @return {string} */
93
+ export function keys (object) {
94
+ return Object.keys(object).filter(
95
+ key => Boolean(object[key])).join(' ')
96
+ }
@@ -0,0 +1,154 @@
1
+ import { listen, reflect, set, get, keys } from './helpers.js'
2
+
3
+ describe('Helpers', () => {
4
+ it('does not allow invalid attribute', () => {
5
+ const element = document.createElement('div')
6
+ element.innerHTML = /* html */`
7
+ <button on-open="click" listen on-click="click" on-click="myMethod"
8
+ on-close="click"></button>
9
+ `
10
+
11
+ listen(element)
12
+
13
+ element.querySelector('button').click()
14
+ expect(!element.hasAttribute('clicked-element')).toBeTruthy()
15
+ })
16
+
17
+ it('does not allow invalid attribute', () => {
18
+ const element = document.createElement('div')
19
+ element.innerHTML = /* html */`
20
+ <button listen ark-on-click="myMethod"></button>
21
+ `
22
+
23
+ listen(element)
24
+
25
+ element.querySelector('button').click()
26
+ expect(!element.hasAttribute('clicked-element')).toBeTruthy()
27
+ })
28
+
29
+ it('attribute ', () => {
30
+ const button = document.createElement('button')
31
+ button.setAttribute('listen', '')
32
+ button.setAttribute('on-abc', 'method')
33
+ button.setAttribute('on-xyz', 'method')
34
+
35
+ const content = document.createElement('div')
36
+ content.appendChild(button)
37
+
38
+ listen(content)
39
+ })
40
+
41
+ it('It does not allow to execute undeclared methods.', () => {
42
+ const element = document.createElement('div')
43
+ element.innerHTML = /* html */`
44
+ <button listen on-click="myMethod"></button>
45
+ `
46
+
47
+ listen(element)
48
+
49
+ element.querySelector('button').click()
50
+ expect(!element.hasAttribute('clicked-element')).toBeTruthy()
51
+ })
52
+
53
+ it('can listen events', () => {
54
+ const element = document.createElement('div')
55
+ element['binding'] = 'listen'
56
+ element.innerHTML = /* html */`
57
+ <button listen on-click="myMethod"></button>
58
+ `
59
+ // @ts-ignore
60
+ element.myMethod = function () {
61
+ element.setAttribute('clicked-element', '')
62
+ }
63
+
64
+ listen(element)
65
+
66
+ element.querySelector('button').click()
67
+ expect(element.hasAttribute('clicked-element')).toBeTruthy()
68
+ })
69
+
70
+ it('ignores missing methods when listening', () => {
71
+ const element = document.createElement('div')
72
+ element['binding'] = 'listen'
73
+ element.innerHTML = /* html */`
74
+ <button listen on-click="missing"></button>
75
+ `
76
+ // @ts-ignore
77
+ element.myMethod = function () {
78
+ element.setAttribute('clicked-element', '')
79
+ }
80
+
81
+ listen(element)
82
+
83
+ element.querySelector('button').click()
84
+ expect(element.hasAttribute('clicked-element')).toBeFalsy()
85
+ })
86
+
87
+ it('can create attribute', () => {
88
+ const element = document.createElement('div')
89
+ const properties = ['myProperty']
90
+
91
+ reflect(element, properties)
92
+
93
+ // @ts-ignore
94
+ element.myProperty = 'value'
95
+
96
+ // @ts-ignore
97
+ expect(element.myProperty).toBe('value')
98
+ expect(element.hasAttribute('my-property')).toBeTruthy()
99
+ })
100
+
101
+ it('cannot create attribute with property undefined', () => {
102
+ const element = document.createElement('div')
103
+ const properties = ['myProperty']
104
+
105
+ reflect(element, properties)
106
+
107
+ // @ts-ignore
108
+ element.myProperty = undefined
109
+
110
+ // @ts-ignore
111
+ expect(element.myProperty).toBe('')
112
+ expect(!element.hasAttribute('my-property')).toBeTruthy()
113
+ })
114
+
115
+ it('sets an object properties by a given path', () => {
116
+ const object = {}
117
+
118
+ set(object, 'value.data', 'Hello')
119
+
120
+ expect(object.value.data).toEqual('Hello')
121
+ })
122
+
123
+ it('gets an object properties by a given path', () => {
124
+ const object = {state: {data: {value: 25}}, data: 13}
125
+
126
+ let value = get(object, 'data')
127
+
128
+ expect(value).toEqual(13)
129
+
130
+ value = get(object, 'state.data.value')
131
+
132
+ expect(value).toEqual(25)
133
+
134
+ const fallback = get(object, 'state.missing.value', 7)
135
+
136
+ expect(fallback).toEqual(7)
137
+ })
138
+
139
+ it('retrieves the object keys of truthy values', () => {
140
+ const background = 'primary'
141
+ const shadow = 'small'
142
+ const color = ''
143
+
144
+ const classes = {
145
+ [`background-${background}`]: background,
146
+ [`color-${color}`]: color,
147
+ [`shadow-${shadow}`]: shadow,
148
+ }
149
+
150
+ let result = keys(classes)
151
+
152
+ expect(result).toEqual('background-primary shadow-small')
153
+ })
154
+ })
@@ -0,0 +1,5 @@
1
+ export * from './define.js'
2
+ export * from './format.js'
3
+ export * from './helpers.js'
4
+ export * from './slots.js'
5
+ export * from './uuid.js'