js-kompo-utils 1.0.0

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 (67) hide show
  1. package/README.md +1 -0
  2. package/app.js +22 -0
  3. package/components/VlApexChart.vue +89 -0
  4. package/components/VlCKEditorTemplate.vue +52 -0
  5. package/components/VlCKEditorWithVars.vue +80 -0
  6. package/components/VlCollapsible.vue +91 -0
  7. package/components/VlCopyBox.vue +61 -0
  8. package/components/VlCreditCardInput.vue +104 -0
  9. package/components/VlDateMonthDay.vue +327 -0
  10. package/components/VlDateTextInput.vue +151 -0
  11. package/components/VlInternationalPhoneInput.vue +902 -0
  12. package/components/VlPartialDate.vue +283 -0
  13. package/components/VlPasswordInput.vue +358 -0
  14. package/components/VlQrCodeScanner.vue +91 -0
  15. package/components/VlSwipeableTab.vue +35 -0
  16. package/components/VlSwipeableTabs.vue +325 -0
  17. package/components/VlText.vue +72 -0
  18. package/components/VlTutorialStepBuilder.vue +671 -0
  19. package/components/VlValidatedInput.vue +262 -0
  20. package/components/data/countries.js +253 -0
  21. package/components/getAllDefaultComponents.js +12 -0
  22. package/docs/architecture.md +71 -0
  23. package/docs/tutorials.md +247 -0
  24. package/docs/usage.md +128 -0
  25. package/functions/index.js +7 -0
  26. package/functions/inputs.js +64 -0
  27. package/functions/loading.js +20 -0
  28. package/kompo-ckeditor-1.0.17/index.js +5 -0
  29. package/kompo-ckeditor-1.0.17/komponents/CKEditor.vue +36 -0
  30. package/kompo-ckeditor-1.0.17/komponents/CKEditorExtended.vue +66 -0
  31. package/kompo-ckeditor-1.0.17/komponents/EditableCKEditor.vue +42 -0
  32. package/kompo-ckeditor-1.0.17/komponents/TranslatableEditor.vue +28 -0
  33. package/kompo-ckeditor-1.0.17/mixins/CKEditor.js +291 -0
  34. package/kompo-ckeditor-1.0.17/mixins/CKEditorUtilities.js +52 -0
  35. package/kompo-ckeditor-1.0.17/mixins/CkUploadAdapter.js +95 -0
  36. package/kompo-ckeditor-1.0.17/mixins/translations/en.js +1 -0
  37. package/kompo-ckeditor-1.0.17/mixins/translations/fr.js +1 -0
  38. package/kompo-ckeditor-1.0.17/plugins/JsonBlockCommand.js +100 -0
  39. package/kompo-ckeditor-1.0.17/plugins/JsonBlockEditing.js +225 -0
  40. package/kompo-ckeditor-1.0.17/plugins/JsonBlockUi.js +21 -0
  41. package/kompo-ckeditor-1.0.17/plugins/MentionPlugin.js +118 -0
  42. package/orchestrator/KompoJsBuilder.js +333 -0
  43. package/package.json +25 -0
  44. package/raw_animations/count-up-animation.js +220 -0
  45. package/raw_animations/magic-poof.js +210 -0
  46. package/tutorials/TutorialConfig.js +34 -0
  47. package/tutorials/TutorialEngine.js +448 -0
  48. package/tutorials/index.js +2 -0
  49. package/tutorials/modules/AutoNext.js +86 -0
  50. package/tutorials/modules/Avatar.js +94 -0
  51. package/tutorials/modules/Bubble.js +617 -0
  52. package/tutorials/modules/Cursor.js +315 -0
  53. package/tutorials/modules/Highlight.js +282 -0
  54. package/tutorials/modules/HoverForcer.js +250 -0
  55. package/tutorials/modules/Mobile.js +148 -0
  56. package/tutorials/modules/Options.js +98 -0
  57. package/tutorials/modules/Overlay.js +142 -0
  58. package/tutorials/modules/Scroll.js +37 -0
  59. package/tutorials/modules/Typewriter.js +132 -0
  60. package/tutorials/step-builder/step-builder-bridge.js +436 -0
  61. package/tutorials/step-builder/utils/bezier-editor.js +379 -0
  62. package/tutorials/step-builder/utils/code-generator.js +127 -0
  63. package/tutorials/step-builder/utils/element-picker.js +125 -0
  64. package/tutorials/tutorial.scss +816 -0
  65. package/tutorials/utils/selectors.js +225 -0
  66. package/tutorials/utils/stability.js +76 -0
  67. package/tutorials/utils/svg.js +46 -0
@@ -0,0 +1,91 @@
1
+ <template>
2
+ <div v-if="isVisible">
3
+
4
+ <p class="px-4 py-2 rounded-xl my-4" :class="resultClass" v-html="result"/>
5
+
6
+ <qrcode-stream :camera="camera" @decode="onDecode" @init="onInit" />
7
+
8
+ <!--<p class="p-4 rounded-xl my-4" :class="resultClass" v-html="result"/>-->
9
+
10
+ </div>
11
+ </template>
12
+
13
+ <script>
14
+
15
+ import Trigger from 'vue-kompo/js/form/mixins/Trigger'
16
+
17
+ export default {
18
+ mixins: [Trigger],
19
+
20
+ data () {
21
+ return {
22
+ result: '',
23
+ resultClass: '',
24
+ error: '',
25
+ camera: 'auto',
26
+ isVisible: false,
27
+ }
28
+ },
29
+
30
+ mounted(){
31
+ setTimeout(() => {
32
+ this.isVisible = true
33
+ window.isCameraActive = true
34
+ setInterval(() => this.checkCameraActive(), 2000)
35
+ }, 1000)
36
+ },
37
+
38
+ methods: {
39
+ checkCameraActive(){
40
+ this.isVisible = window.isCameraActive
41
+ },
42
+ onDecode (qrCode) {
43
+ axios({
44
+ url: this.$_config('checkRoute')+'/'+qrCode,
45
+ method: 'POST',
46
+ }).then(r => {
47
+ this.$kompo.vlFillModal(r, 'some-kompo-id')
48
+ this.camera = 'off'
49
+ setTimeout(() => this.camera = 'auto', 1000)
50
+ })
51
+ },
52
+
53
+ async onInit (promise) {
54
+ try {
55
+ await promise
56
+
57
+ this.result = this.$_config('readyMsg');
58
+ this.resultClass = 'bg-level5 text-white';
59
+
60
+ } catch (error) {
61
+ if (error.name === 'NotAllowedError') {
62
+ this.error = "ERROR: you need to grant camera access permission"
63
+ } else if (error.name === 'NotFoundError') {
64
+ this.error = "ERROR: no camera on this device"
65
+ } else if (error.name === 'NotSupportedError') {
66
+ his.error = "ERROR: secure context required (HTTPS, localhost)"
67
+ } else if (error.name === 'NotReadableError') {
68
+ this.error = "ERROR: is the camera already in use?"
69
+ } else if (error.name === 'OverconstrainedError') {
70
+ this.error = "ERROR: installed cameras are not suitable"
71
+ } else if (error.name === 'StreamApiNotSupportedError') {
72
+ this.error = "ERROR: Stream API is not supported in this browser"
73
+ } else if (error.name === 'InsecureContextError') {
74
+ this.error = 'ERROR: Camera access is only permitted in secure context. Use HTTPS or localhost rather than HTTP.';
75
+ } else {
76
+ this.error = `ERROR: Camera error (${error.name})`;
77
+ }
78
+ this.resultClass = 'bg-danger text-white';
79
+ this.result = this.error;
80
+ }
81
+ }
82
+ }
83
+ }
84
+ </script>
85
+
86
+ <style scoped>
87
+ .error {
88
+ font-weight: bold;
89
+ color: red;
90
+ }
91
+ </style>
@@ -0,0 +1,35 @@
1
+ <template>
2
+ <section
3
+ v-if="!isDisabled"
4
+ :aria-hidden="!activeTab"
5
+ v-show="swipeable || activeTab"
6
+ v-bind="$_layoutWrapperAttributes"
7
+ class="vlTab"
8
+ :class="{ 'vlTab--swipeable': swipeable, 'vlTab--active': activeTab, 'vlTab--collapsed': collapsed }"
9
+ role="tabpanel">
10
+
11
+ <component
12
+ v-bind="$_attributes(tab)"
13
+ v-for="(tab,index) in elements"
14
+ :key="index" />
15
+
16
+ </section>
17
+ </template>
18
+
19
+ <script>
20
+ import Layout from 'vue-kompo/js/form/mixins/Layout'
21
+
22
+ export default {
23
+ mixins: [Layout],
24
+ props: {
25
+ activeTab: { type: Boolean, required: true },
26
+ swipeable: { type: Boolean, default: false },
27
+ collapsed: { type: Boolean, default: false },
28
+ },
29
+ computed: {
30
+ isDisabled(){
31
+ return this.$_config('tabDisabled')
32
+ },
33
+ },
34
+ }
35
+ </script>
@@ -0,0 +1,325 @@
1
+ <template>
2
+ <div class="vlTabs"
3
+ v-bind="$_layoutWrapperAttributes"
4
+ v-show="!$_hidden">
5
+ <ul role="tablist" class="flex">
6
+ <li
7
+ v-for="(element, index) in elements"
8
+ :key="index"
9
+ role="presentation"
10
+ >
11
+ <vl-form-tab-label
12
+ :activeTab="tabActive(index)"
13
+ :selectedClass="$_selectedClass"
14
+ :unselectedClass="$_unselectedClass"
15
+ :commonClass="$_commonClass"
16
+ :disabledClass="disabledClass"
17
+ :vkompo="element"
18
+ @selectTab="selectTab(index)" />
19
+ </li>
20
+ </ul>
21
+ <div class="vlTabContent" :class="{ 'vlTabContent--swipeable': isSwipeable }">
22
+ <div v-if="isSwipeable"
23
+ class="vlTabSwipeContainer"
24
+ :style="swipeContainerStyle"
25
+ @touchstart="onTouchStart"
26
+ @touchmove="onTouchMove"
27
+ @touchend="onTouchEnd">
28
+ <component
29
+ v-for="(tab, index) in elements"
30
+ :key="index"
31
+ :activeTab="tabActive(index)"
32
+ :collapsed="!tabActive(index)"
33
+ :swipeable="true"
34
+ v-bind="$_attributes(tab)"/>
35
+ </div>
36
+ <template v-else>
37
+ <component
38
+ v-for="(tab,index) in elements"
39
+ :key="index"
40
+ :activeTab="tabActive(index)"
41
+ v-bind="$_attributes(tab)"/>
42
+ </template>
43
+ </div>
44
+ </div>
45
+ </template>
46
+
47
+ <script>
48
+ import Layout from 'vue-kompo/js/form/mixins/Layout'
49
+ import HasSelectedClass from 'vue-kompo/js/form/mixins/HasSelectedClass'
50
+
51
+ export default {
52
+ mixins: [Layout, HasSelectedClass],
53
+ data(){
54
+ return {
55
+ activeTab: 0,
56
+ // Swipe state
57
+ swipeOffset: 0,
58
+ isSwiping: false,
59
+ isAnimating: false,
60
+ startX: 0,
61
+ startY: 0,
62
+ directionLocked: false,
63
+ }
64
+ },
65
+ computed: {
66
+ defaultActiveTab(){
67
+ return this.$_config('activeTab')
68
+ },
69
+ tabParamKey(){
70
+ return 'tab_number'
71
+ },
72
+ selectId(){
73
+ return this.$_config('selectId')
74
+ },
75
+ disabledClass(){
76
+ return this.$_config('disabledClass')
77
+ },
78
+ isSwipeable(){
79
+ return !!this.$_config('swipeable')
80
+ },
81
+ isMobile(){
82
+ return typeof window !== 'undefined' && window.matchMedia('(max-width: 1023px)').matches
83
+ },
84
+ swipeContainerStyle(){
85
+ var baseOffset = -this.activeTab * 100
86
+
87
+ if (!this.isMobile) {
88
+ return {
89
+ transform: 'translateX(' + baseOffset + '%)',
90
+ }
91
+ }
92
+
93
+ if (this.isSwiping) {
94
+ return {
95
+ transform: 'translateX(calc(' + baseOffset + '% + ' + this.swipeOffset + 'px))',
96
+ transition: 'none',
97
+ }
98
+ }
99
+
100
+ return {
101
+ transform: 'translateX(' + baseOffset + '%)',
102
+ transition: this.isAnimating ? 'transform 0.3s ease' : 'none',
103
+ }
104
+ },
105
+ },
106
+ methods:{
107
+ $_validate(errors) {
108
+ Layout.methods.$_validate.call(this, errors)
109
+ this.elements.forEach( (tab, index) => {
110
+ var errors = []
111
+ tab.$_getErrors(errors)
112
+ if(errors.length)
113
+ this.activeTab = index
114
+ })
115
+ },
116
+ selectTab(index) {
117
+ if (this.activeTab === index) return
118
+
119
+ if (this.isSwipeable) {
120
+ this.isAnimating = true
121
+ }
122
+
123
+ this.activeTab = index
124
+ this.persistActiveTab()
125
+ this.syncSelect()
126
+
127
+ this.$_runOwnInteractions('click')
128
+
129
+ if (this.isSwipeable) {
130
+ setTimeout(() => {
131
+ this.isAnimating = false
132
+ }, 300)
133
+ }
134
+ },
135
+ tabActive(index) {
136
+ return index == this.activeTab
137
+ },
138
+ tabCollapsed(index) {
139
+ if (!this.isMobile) return false
140
+ if (index === this.activeTab) return false
141
+ if (this.isSwiping || this.isAnimating) return false
142
+ return true
143
+ },
144
+ tabDisabled(index) {
145
+ return this.elements[index].isDisabled
146
+ },
147
+
148
+ // --- Swipe methods ---
149
+
150
+ beginSwipe(startX, startY) {
151
+ this.startX = startX
152
+ this.startY = startY
153
+ this.swipeOffset = 0
154
+ this.isSwiping = false
155
+ this.directionLocked = false
156
+ },
157
+
158
+ updateSwipe(clientX, clientY, preventDefault) {
159
+ if (this.isAnimating) return
160
+
161
+ var deltaX = clientX - this.startX
162
+ var deltaY = clientY - this.startY
163
+
164
+ // Lock direction on first significant movement
165
+ if (!this.directionLocked) {
166
+ if (Math.abs(deltaX) < 5 && Math.abs(deltaY) < 5) return
167
+
168
+ if (Math.abs(deltaY) > Math.abs(deltaX)) {
169
+ // Vertical scroll — do nothing, let browser handle it
170
+ this.directionLocked = true
171
+ this.isSwiping = false
172
+ return
173
+ }
174
+
175
+ this.directionLocked = true
176
+ this.isSwiping = true
177
+ }
178
+
179
+ if (!this.isSwiping) return
180
+
181
+ if (preventDefault)
182
+ preventDefault()
183
+
184
+ // Boundary resistance: dampen swipe at edges
185
+ if (this.activeTab === 0 && deltaX > 0) {
186
+ deltaX = deltaX * 0.3
187
+ } else if (this.activeTab === this.elements.length - 1 && deltaX < 0) {
188
+ deltaX = deltaX * 0.3
189
+ }
190
+
191
+ this.swipeOffset = deltaX
192
+ },
193
+
194
+ completeSwipe() {
195
+ if (!this.isSwiping) {
196
+ this.directionLocked = false
197
+ return
198
+ }
199
+
200
+ var container = this.$el.querySelector('.vlTabContent')
201
+ var containerWidth = container ? container.offsetWidth : 1
202
+ var swipePercent = Math.abs(this.swipeOffset) / containerWidth
203
+ var swipedLeft = this.swipeOffset < 0
204
+ var swipedRight = this.swipeOffset > 0
205
+
206
+ this.isSwiping = false
207
+ this.isAnimating = true
208
+
209
+ if (swipePercent >= 0.4) {
210
+ // Find next valid (non-disabled) tab
211
+ var nextIndex = this.activeTab
212
+
213
+ if (swipedLeft && this.activeTab < this.elements.length - 1) {
214
+ nextIndex = this.findNextEnabledTab(this.activeTab, 1)
215
+ } else if (swipedRight && this.activeTab > 0) {
216
+ nextIndex = this.findNextEnabledTab(this.activeTab, -1)
217
+ }
218
+
219
+ if (nextIndex !== this.activeTab) {
220
+ this.activeTab = nextIndex
221
+ this.persistActiveTab()
222
+ this.syncSelect()
223
+ this.$_runOwnInteractions('click')
224
+ }
225
+ }
226
+ // else: snap back (activeTab unchanged, animation brings it back)
227
+
228
+ this.swipeOffset = 0
229
+
230
+ setTimeout(() => {
231
+ this.isAnimating = false
232
+ }, 300)
233
+ },
234
+
235
+ onTouchStart(e) {
236
+ if (this.isAnimating) return
237
+
238
+ var touch = e.touches[0]
239
+ this.beginSwipe(touch.clientX, touch.clientY)
240
+ },
241
+
242
+ onTouchMove(e) {
243
+ var touch = e.touches[0]
244
+ this.updateSwipe(touch.clientX, touch.clientY, () => e.preventDefault())
245
+ },
246
+
247
+ onTouchEnd() {
248
+ this.completeSwipe()
249
+ },
250
+
251
+ findNextEnabledTab(fromIndex, direction) {
252
+ var nextIndex = fromIndex + direction
253
+
254
+ while (nextIndex >= 0 && nextIndex < this.elements.length) {
255
+ if (!this.elements[nextIndex].isDisabled) {
256
+ return nextIndex
257
+ }
258
+ nextIndex += direction
259
+ }
260
+
261
+ return fromIndex // No valid tab found, stay put
262
+ },
263
+
264
+ persistActiveTab() {
265
+ if (!this.tabParamKey || typeof window === 'undefined') return
266
+
267
+ var url = new URL(window.location.href)
268
+ url.searchParams.set(this.tabParamKey, this.activeTab)
269
+ window.history.replaceState({}, '', url.toString())
270
+ },
271
+
272
+ getPersistedTab() {
273
+ if (!this.tabParamKey || typeof window === 'undefined') return null
274
+
275
+ var params = new URLSearchParams(window.location.search)
276
+ var tabParam = params.get(this.tabParamKey)
277
+
278
+ if (tabParam === null) return null
279
+
280
+ var parsed = parseInt(tabParam, 10)
281
+ return isNaN(parsed) ? null : parsed
282
+ },
283
+
284
+ syncSelect() {
285
+ if (!this.selectId || typeof window === 'undefined') return
286
+
287
+ this.$nextTick(() => {
288
+ var inputEl = document.getElementById(this.selectId)
289
+ if (!inputEl) return
290
+
291
+ // id is on the <input>, walk up DOM to find a Vue component root
292
+ var el = inputEl.parentElement
293
+ while (el && !el.__vue__) {
294
+ el = el.parentElement
295
+ }
296
+ if (!el) return
297
+
298
+ // Walk up Vue component tree to find the Kompo Select
299
+ var vue = el.__vue__
300
+ while (vue && typeof vue.select !== 'function') {
301
+ vue = vue.$parent
302
+ }
303
+ if (!vue || !vue.component || !vue.component.options) return
304
+
305
+ var option = vue.component.options[this.activeTab]
306
+ if (option) {
307
+ vue.$set(vue.component, 'value', [option])
308
+ }
309
+ })
310
+ },
311
+ },
312
+ created(){
313
+ var persistedTab = this.getPersistedTab()
314
+
315
+ if(persistedTab !== null){
316
+ this.activeTab = persistedTab
317
+ }else if(this.defaultActiveTab !== undefined && this.defaultActiveTab !== null){
318
+ this.activeTab = this.defaultActiveTab
319
+ }
320
+ },
321
+ mounted(){
322
+ this.syncSelect()
323
+ },
324
+ }
325
+ </script>
@@ -0,0 +1,72 @@
1
+ <template>
2
+ <div>
3
+ <div
4
+ v-if="!$_displayNone"
5
+ v-show="!$_hidden"
6
+ v-bind="$_attributes"
7
+ v-html="content"
8
+ />
9
+ <button type="button" :style="buttonStyle" :class="buttonClass" v-if="showMoreBtn" @click.stop="toggleShowingMore">{{showingMore ? showLessText : showMoreText}}</button>
10
+ </div>
11
+ </template>
12
+
13
+ <script>
14
+ import Other from 'vue-kompo/js/form/mixins/Other'
15
+
16
+ export default {
17
+ mixins: [Other],
18
+ methods: {
19
+ toggleShowingMore() {
20
+ this.showingMore = !this.showingMore;
21
+ }
22
+ },
23
+ computed: {
24
+ content() {
25
+ const originalContent = this.$_config('content');
26
+ let content = originalContent;
27
+
28
+ if (this.maxLines != null && !this.showingMore) {
29
+ content = content.split('\r\n').slice(0, this.maxLines).join('\r\n');
30
+ }
31
+
32
+ if (this.maxChars != null && !this.showingMore) {
33
+ content = content.slice(0, this.maxChars);
34
+ }
35
+
36
+ if (originalContent != content) {
37
+ content += '...';
38
+ }
39
+
40
+ return content;
41
+ },
42
+ showMoreBtn() {
43
+ return (this.maxLines != null || this.maxChars != null) &&
44
+ (this.$_config('content').length != this.content.length || this.showingMore);
45
+ }
46
+ },
47
+ data() {
48
+ return {
49
+ maxLines: null,
50
+ maxChars: null,
51
+
52
+ showMoreText: '',
53
+ showLessText: '',
54
+
55
+ buttonClass: '',
56
+ buttonStyle: '',
57
+
58
+ showingMore: false,
59
+ }
60
+ },
61
+ mounted() {
62
+ this.maxLines = this.$_config('maxLines') ?? this.maxLines;
63
+ this.maxChars = this.$_config('maxChars') ?? this.maxChars;
64
+
65
+ this.showMoreText = this.$_config('showMoreText') ?? this.showMoreText;
66
+ this.showLessText = this.$_config('showLessText') ?? this.showLessText;
67
+
68
+ this.buttonClass = this.$_config('buttonClass') ?? this.buttonClass;
69
+ this.buttonStyle = this.$_config('buttonStyle') ?? this.buttonStyle;
70
+ },
71
+ }
72
+ </script>