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.
- package/README.md +1 -0
- package/app.js +22 -0
- package/components/VlApexChart.vue +89 -0
- package/components/VlCKEditorTemplate.vue +52 -0
- package/components/VlCKEditorWithVars.vue +80 -0
- package/components/VlCollapsible.vue +91 -0
- package/components/VlCopyBox.vue +61 -0
- package/components/VlCreditCardInput.vue +104 -0
- package/components/VlDateMonthDay.vue +327 -0
- package/components/VlDateTextInput.vue +151 -0
- package/components/VlInternationalPhoneInput.vue +902 -0
- package/components/VlPartialDate.vue +283 -0
- package/components/VlPasswordInput.vue +358 -0
- package/components/VlQrCodeScanner.vue +91 -0
- package/components/VlSwipeableTab.vue +35 -0
- package/components/VlSwipeableTabs.vue +325 -0
- package/components/VlText.vue +72 -0
- package/components/VlTutorialStepBuilder.vue +671 -0
- package/components/VlValidatedInput.vue +262 -0
- package/components/data/countries.js +253 -0
- package/components/getAllDefaultComponents.js +12 -0
- package/docs/architecture.md +71 -0
- package/docs/tutorials.md +247 -0
- package/docs/usage.md +128 -0
- package/functions/index.js +7 -0
- package/functions/inputs.js +64 -0
- package/functions/loading.js +20 -0
- package/kompo-ckeditor-1.0.17/index.js +5 -0
- package/kompo-ckeditor-1.0.17/komponents/CKEditor.vue +36 -0
- package/kompo-ckeditor-1.0.17/komponents/CKEditorExtended.vue +66 -0
- package/kompo-ckeditor-1.0.17/komponents/EditableCKEditor.vue +42 -0
- package/kompo-ckeditor-1.0.17/komponents/TranslatableEditor.vue +28 -0
- package/kompo-ckeditor-1.0.17/mixins/CKEditor.js +291 -0
- package/kompo-ckeditor-1.0.17/mixins/CKEditorUtilities.js +52 -0
- package/kompo-ckeditor-1.0.17/mixins/CkUploadAdapter.js +95 -0
- package/kompo-ckeditor-1.0.17/mixins/translations/en.js +1 -0
- package/kompo-ckeditor-1.0.17/mixins/translations/fr.js +1 -0
- package/kompo-ckeditor-1.0.17/plugins/JsonBlockCommand.js +100 -0
- package/kompo-ckeditor-1.0.17/plugins/JsonBlockEditing.js +225 -0
- package/kompo-ckeditor-1.0.17/plugins/JsonBlockUi.js +21 -0
- package/kompo-ckeditor-1.0.17/plugins/MentionPlugin.js +118 -0
- package/orchestrator/KompoJsBuilder.js +333 -0
- package/package.json +25 -0
- package/raw_animations/count-up-animation.js +220 -0
- package/raw_animations/magic-poof.js +210 -0
- package/tutorials/TutorialConfig.js +34 -0
- package/tutorials/TutorialEngine.js +448 -0
- package/tutorials/index.js +2 -0
- package/tutorials/modules/AutoNext.js +86 -0
- package/tutorials/modules/Avatar.js +94 -0
- package/tutorials/modules/Bubble.js +617 -0
- package/tutorials/modules/Cursor.js +315 -0
- package/tutorials/modules/Highlight.js +282 -0
- package/tutorials/modules/HoverForcer.js +250 -0
- package/tutorials/modules/Mobile.js +148 -0
- package/tutorials/modules/Options.js +98 -0
- package/tutorials/modules/Overlay.js +142 -0
- package/tutorials/modules/Scroll.js +37 -0
- package/tutorials/modules/Typewriter.js +132 -0
- package/tutorials/step-builder/step-builder-bridge.js +436 -0
- package/tutorials/step-builder/utils/bezier-editor.js +379 -0
- package/tutorials/step-builder/utils/code-generator.js +127 -0
- package/tutorials/step-builder/utils/element-picker.js +125 -0
- package/tutorials/tutorial.scss +816 -0
- package/tutorials/utils/selectors.js +225 -0
- package/tutorials/utils/stability.js +76 -0
- package/tutorials/utils/svg.js +46 -0
package/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Kompo js utils
|
package/app.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { KompoJsConfigBuilder, KompoJsInitializer } from './orchestrator/KompoJsBuilder.js';
|
|
2
|
+
import VueQrcodeReader from 'vue-qrcode-reader';
|
|
3
|
+
import functions from './functions/index.js';
|
|
4
|
+
import { getAllDefaultComponents } from './components/getAllDefaultComponents.js';
|
|
5
|
+
|
|
6
|
+
const defaultConfig = new KompoJsConfigBuilder()
|
|
7
|
+
.withJquery()
|
|
8
|
+
.withCkEditor()
|
|
9
|
+
.withKompoMaps()
|
|
10
|
+
.addVuePlugin(VueQrcodeReader)
|
|
11
|
+
.mergeWindowUtils(functions)
|
|
12
|
+
.addComponents(getAllDefaultComponents())
|
|
13
|
+
.addFilesToImport([
|
|
14
|
+
() => require('./raw_animations/count-up-animation.js'),
|
|
15
|
+
() => require('./raw_animations/magic-poof.js')
|
|
16
|
+
]);
|
|
17
|
+
|
|
18
|
+
export {
|
|
19
|
+
defaultConfig,
|
|
20
|
+
KompoJsInitializer,
|
|
21
|
+
KompoJsConfigBuilder
|
|
22
|
+
};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
v-if="!$_displayNone"
|
|
4
|
+
v-show="!$_hidden"
|
|
5
|
+
v-bind="$_attributes"
|
|
6
|
+
>
|
|
7
|
+
<div ref="chart"></div>
|
|
8
|
+
</div>
|
|
9
|
+
</template>
|
|
10
|
+
|
|
11
|
+
<script>
|
|
12
|
+
import ApexCharts from 'apexcharts'
|
|
13
|
+
import Other from 'vue-kompo/js/form/mixins/Other'
|
|
14
|
+
|
|
15
|
+
export default {
|
|
16
|
+
mixins: [Other],
|
|
17
|
+
data() {
|
|
18
|
+
return {
|
|
19
|
+
chartInstance: null,
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
methods: {
|
|
23
|
+
formatCompact(val) {
|
|
24
|
+
if (val >= 1000000) return (val / 1000000).toFixed(1).replace(/\.0$/, '') + 'M'
|
|
25
|
+
if (val >= 1000) return (val / 1000).toFixed(1).replace(/\.0$/, '') + 'k'
|
|
26
|
+
return val
|
|
27
|
+
},
|
|
28
|
+
applyFormatters(options) {
|
|
29
|
+
const applyToAxis = (axis) => {
|
|
30
|
+
if (axis && axis.compactNumbers) {
|
|
31
|
+
axis.labels = axis.labels || {}
|
|
32
|
+
axis.labels.formatter = (val) => this.formatCompact(val)
|
|
33
|
+
delete axis.compactNumbers
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
if (Array.isArray(options.yaxis)) {
|
|
37
|
+
options.yaxis.forEach(applyToAxis)
|
|
38
|
+
} else if (options.yaxis) {
|
|
39
|
+
applyToAxis(options.yaxis)
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
resolveRawFunctions(obj) {
|
|
43
|
+
const RAW_PREFIX = 'APEXCHART_RAW:'
|
|
44
|
+
if (typeof obj === 'string' && obj.startsWith(RAW_PREFIX)) {
|
|
45
|
+
const fnStr = obj.slice(RAW_PREFIX.length)
|
|
46
|
+
try {
|
|
47
|
+
return new Function('return (' + fnStr + ')')()
|
|
48
|
+
} catch (e) {
|
|
49
|
+
console.warn('Failed to parse APEXCHART_RAW function:', fnStr, e)
|
|
50
|
+
return undefined
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (Array.isArray(obj)) return obj.map(v => this.resolveRawFunctions(v))
|
|
54
|
+
if (obj && typeof obj === 'object') {
|
|
55
|
+
const resolved = {}
|
|
56
|
+
for (const key in obj) resolved[key] = this.resolveRawFunctions(obj[key])
|
|
57
|
+
return resolved
|
|
58
|
+
}
|
|
59
|
+
return obj
|
|
60
|
+
},
|
|
61
|
+
resolveCssVars(obj) {
|
|
62
|
+
if (typeof obj === 'string' && obj.startsWith('var(--')) {
|
|
63
|
+
const varName = obj.slice(4, -1)
|
|
64
|
+
return getComputedStyle(document.documentElement).getPropertyValue(varName).trim()
|
|
65
|
+
}
|
|
66
|
+
if (Array.isArray(obj)) return obj.map(v => this.resolveCssVars(v))
|
|
67
|
+
if (obj && typeof obj === 'object') {
|
|
68
|
+
const resolved = {}
|
|
69
|
+
for (const key in obj) resolved[key] = this.resolveCssVars(obj[key])
|
|
70
|
+
return resolved
|
|
71
|
+
}
|
|
72
|
+
return obj
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
mounted() {
|
|
76
|
+
let options = this.$_config('chartOptions') || {}
|
|
77
|
+
options = this.resolveCssVars(options)
|
|
78
|
+
options = this.resolveRawFunctions(options)
|
|
79
|
+
this.applyFormatters(options)
|
|
80
|
+
this.chartInstance = new ApexCharts(this.$refs.chart, options)
|
|
81
|
+
this.chartInstance.render()
|
|
82
|
+
},
|
|
83
|
+
beforeDestroy() {
|
|
84
|
+
if (this.chartInstance) {
|
|
85
|
+
this.chartInstance.destroy()
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
}
|
|
89
|
+
</script>
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div :class="'card-gray-100 p-4 ' + $_config('containerClass')">
|
|
3
|
+
<div>
|
|
4
|
+
<div class="vlFormLabel" v-if="!$_config('removeLabel')" />
|
|
5
|
+
<div class="py-2">
|
|
6
|
+
<div v-if="!$_config('withoutHeight')" class="h-24"></div>
|
|
7
|
+
|
|
8
|
+
<vl-form-field v-bind="$_wrapperAttributes">
|
|
9
|
+
<vlLocaleTabs :locales="locales" :activeLocale="activeLocale" @changeLocale="changeTab" />
|
|
10
|
+
<ckeditor class="vlFormControl" ref="content" v-model="currentTranslation" v-bind="$_attributes"
|
|
11
|
+
v-on="$_events" @keydown.stop :editor="editor" :config="editorConfig" />
|
|
12
|
+
</vl-form-field>
|
|
13
|
+
</div>
|
|
14
|
+
</div>
|
|
15
|
+
<div class="py-3">
|
|
16
|
+
<div class="vlFormLabel" v-if="$_config('titleVariables')">{{ $_config('titleVariables') }}</div>
|
|
17
|
+
<div class="flex flex-wrap gap-4 mt-2">
|
|
18
|
+
<div class="flex-1" v-for="(variables, typeLabel) in allVariables" :key="typeLabel">
|
|
19
|
+
<vl-dropdown :vkompo="variables" />
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
</template>
|
|
25
|
+
|
|
26
|
+
<script>
|
|
27
|
+
import Translatable from 'vue-kompo/js/form/mixins/Translatable'
|
|
28
|
+
import CKEditor from '../kompo-ckeditor-1.0.17/mixins/CKEditor'
|
|
29
|
+
import CKEditorUtilities from '../kompo-ckeditor-1.0.17/mixins/CKEditorUtilities'
|
|
30
|
+
|
|
31
|
+
export default {
|
|
32
|
+
mixins: [CKEditor, Translatable, CKEditorUtilities],
|
|
33
|
+
data() {
|
|
34
|
+
return {
|
|
35
|
+
allVariables: [],
|
|
36
|
+
variableToInsert: null,
|
|
37
|
+
editorInitialized: false
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
created() {
|
|
41
|
+
this.allVariables = this.$_config('variables')
|
|
42
|
+
this.$_vlOn("insertVariable", (payload) => {
|
|
43
|
+
if (!payload['uniqueid'] || this.$_config('uniqueId') == payload['uniqueid']) {
|
|
44
|
+
this.insertVariable(payload);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
},
|
|
48
|
+
computed: {
|
|
49
|
+
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
</script>
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div :class="$_config('containerClass')">
|
|
3
|
+
<vl-form-field v-bind="$_wrapperAttributes">
|
|
4
|
+
<vlLocaleTabs :locales="locales" :activeLocale="activeLocale" @changeLocale="changeTab" />
|
|
5
|
+
<ckeditor class="vlFormControl" ref="content" v-model="currentTranslation" v-bind="$_attributes"
|
|
6
|
+
v-on="$_events" @keydown.stop :editor="editor" :config="editorConfig" />
|
|
7
|
+
</vl-form-field>
|
|
8
|
+
<div class="vlFormLabel" v-if="$_config('titleVariables')">
|
|
9
|
+
{{ $_config("titleVariables") }}
|
|
10
|
+
</div>
|
|
11
|
+
<div class="flex flex-wrap gap-4 mt-2">
|
|
12
|
+
<div class="flex-1" v-for="(variables, typeLabel) in allVariables" :key="typeLabel">
|
|
13
|
+
<vl-dropdown :vkompo="variables" />
|
|
14
|
+
</div>
|
|
15
|
+
</div>
|
|
16
|
+
</div>
|
|
17
|
+
</template>
|
|
18
|
+
|
|
19
|
+
<script>
|
|
20
|
+
import Translatable from "vue-kompo/js/form/mixins/Translatable";
|
|
21
|
+
import CKEditor from "../kompo-ckeditor-1.0.17/mixins/CKEditor";
|
|
22
|
+
|
|
23
|
+
export default {
|
|
24
|
+
mixins: [CKEditor, Translatable],
|
|
25
|
+
data() {
|
|
26
|
+
return {
|
|
27
|
+
allVariables: [],
|
|
28
|
+
variableToInsert: null,
|
|
29
|
+
};
|
|
30
|
+
},
|
|
31
|
+
methods: {
|
|
32
|
+
insertVariable(payload) {
|
|
33
|
+
var html =
|
|
34
|
+
'<span class="mention" data-mention="' +
|
|
35
|
+
payload.type +
|
|
36
|
+
'">' +
|
|
37
|
+
payload.label +
|
|
38
|
+
"</span>";
|
|
39
|
+
this.insertHtml(html);
|
|
40
|
+
|
|
41
|
+
this.variableToInsert = null;
|
|
42
|
+
},
|
|
43
|
+
insertHtml(html) {
|
|
44
|
+
const editor = this.$refs.content.$_instance;
|
|
45
|
+
editor.model.change((writer) => {
|
|
46
|
+
const viewFragment = editor.data.processor.toView(html);
|
|
47
|
+
const modelFragment = editor.data.toModel(viewFragment);
|
|
48
|
+
editor.model.insertContent(
|
|
49
|
+
modelFragment,
|
|
50
|
+
editor.model.document.selection
|
|
51
|
+
);
|
|
52
|
+
});
|
|
53
|
+
},
|
|
54
|
+
insertText(text) {
|
|
55
|
+
const editor = this.$refs.content.$_instance;
|
|
56
|
+
editor.model.change((writer) => {
|
|
57
|
+
const insertPosition =
|
|
58
|
+
editor.model.document.selection.getFirstPosition();
|
|
59
|
+
writer.insertText(text, insertPosition);
|
|
60
|
+
});
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
created() {
|
|
64
|
+
this.allVariables = this.$_config("variables");
|
|
65
|
+
|
|
66
|
+
this.$_vlOn("insertVariable", (payload) => {
|
|
67
|
+
if (!payload['uniqueid'] || this.$_config('uniqueId') == payload['uniqueid']) {
|
|
68
|
+
this.insertVariable(payload);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
</script>
|
|
74
|
+
|
|
75
|
+
<style scoped>
|
|
76
|
+
.ck-editor__editable_inline {
|
|
77
|
+
min-height: unset !important;
|
|
78
|
+
max-height: unset !important;
|
|
79
|
+
}
|
|
80
|
+
</style>
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div v-bind="$_layoutWrapperAttributes" v-show="!$_hidden" @click="$_clickAction"
|
|
3
|
+
:class="[expanded ? expandedClass : collapsedClass]" v-turbo-click="component.turbo">
|
|
4
|
+
<div @click="toggleSubGroup" class="flex flex-between cursor-pointer items-center">
|
|
5
|
+
<div v-bind="$_attributes(titleEl)" />
|
|
6
|
+
|
|
7
|
+
<div v-html="icon" v-if="icon" :style="{
|
|
8
|
+
transform: expanded ? 'rotate(180deg)' : 'rotate(0deg)',
|
|
9
|
+
transition: 'transform 0.3s ease'
|
|
10
|
+
}" />
|
|
11
|
+
</div>
|
|
12
|
+
<transition name="expand">
|
|
13
|
+
<div v-show="expanded" :class="wrapperElementsClass || ''">
|
|
14
|
+
<template v-for="row in elements">
|
|
15
|
+
<div v-bind="$_attributes(row)" />
|
|
16
|
+
</template>
|
|
17
|
+
</div>
|
|
18
|
+
</transition>
|
|
19
|
+
</div>
|
|
20
|
+
</template>
|
|
21
|
+
|
|
22
|
+
<script>
|
|
23
|
+
import Layout from 'vue-kompo/js/form/mixins/Layout'
|
|
24
|
+
|
|
25
|
+
export default {
|
|
26
|
+
mixins: [Layout],
|
|
27
|
+
data() {
|
|
28
|
+
return {
|
|
29
|
+
expanded: false,
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
created() {
|
|
33
|
+
if (this.$_config('expandedByDefault')) {
|
|
34
|
+
setTimeout(() => {
|
|
35
|
+
this.expanded = true;
|
|
36
|
+
}, this.$_config('expandByDefaultDelay') || 0);
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
mounted() {
|
|
40
|
+
setTimeout(() => {
|
|
41
|
+
if (this.$_config('expandBasedInLinks') && this.links.some(link => window.location.origin + window.location.pathname == link)) {
|
|
42
|
+
this.expanded = true;
|
|
43
|
+
}
|
|
44
|
+
}, 100);
|
|
45
|
+
},
|
|
46
|
+
computed: {
|
|
47
|
+
titleEl() {
|
|
48
|
+
return this.$_config('titleEl');
|
|
49
|
+
},
|
|
50
|
+
idSection() {
|
|
51
|
+
return this.$_config('idSection');
|
|
52
|
+
},
|
|
53
|
+
icon() {
|
|
54
|
+
return this.$_config('iconT');
|
|
55
|
+
},
|
|
56
|
+
expandedClass() {
|
|
57
|
+
return (this.$_config('expandedClass') || 'expanded') + ' ' + this.$_classes;
|
|
58
|
+
},
|
|
59
|
+
collapsedClass() {
|
|
60
|
+
return (this.$_config('collapsedClasses') || 'collapsed') + ' ' + this.$_classes;
|
|
61
|
+
},
|
|
62
|
+
wrapperElementsClass() {
|
|
63
|
+
return this.$_config('wrapperElementsClass');
|
|
64
|
+
},
|
|
65
|
+
links() {
|
|
66
|
+
return this.$_config('links').split(',');
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
methods: {
|
|
70
|
+
toggleSubGroup() {
|
|
71
|
+
this.expanded = !this.expanded;
|
|
72
|
+
},
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
</script>
|
|
76
|
+
|
|
77
|
+
<style>
|
|
78
|
+
.expand-enter-active, .expand-leave-active {
|
|
79
|
+
transition: max-height 0.35s ease, opacity 0.35s ease;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.expand-enter, .expand-leave-to {
|
|
83
|
+
max-height: 0;
|
|
84
|
+
opacity: 0;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.expand-enter-to, .expand-leave {
|
|
88
|
+
max-height: 1000px;
|
|
89
|
+
opacity: 1;
|
|
90
|
+
}
|
|
91
|
+
</style>
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
v-if="!$_displayNone"
|
|
4
|
+
v-show="!$_hidden"
|
|
5
|
+
v-bind="$_attributes"
|
|
6
|
+
class="flex"
|
|
7
|
+
>
|
|
8
|
+
<div
|
|
9
|
+
:style="{tabSize: tabSize}"
|
|
10
|
+
ref="code"
|
|
11
|
+
class="m-4 flex-1"
|
|
12
|
+
v-html="$_label"/>
|
|
13
|
+
<button
|
|
14
|
+
@click.prevent="copyToClipboard"
|
|
15
|
+
class="copy-button"
|
|
16
|
+
:class="buttonRoundedClass"
|
|
17
|
+
:aria-label="balloonLabel" data-balloon-pos="up-right"
|
|
18
|
+
><svg
|
|
19
|
+
class="w-8 h-8"
|
|
20
|
+
fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg>
|
|
21
|
+
</button>
|
|
22
|
+
</div>
|
|
23
|
+
</template>
|
|
24
|
+
|
|
25
|
+
<script>
|
|
26
|
+
|
|
27
|
+
import Other from 'vue-kompo/js/form/mixins/Other'
|
|
28
|
+
|
|
29
|
+
export default {
|
|
30
|
+
mixins: [Other],
|
|
31
|
+
data() {
|
|
32
|
+
return {
|
|
33
|
+
highlightedCode: '',
|
|
34
|
+
toCopyCode: '',
|
|
35
|
+
balloonLabel: 'Copy',
|
|
36
|
+
buttonRoundedClass: 'rounded-r-lg',
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
computed: {
|
|
40
|
+
|
|
41
|
+
},
|
|
42
|
+
methods: {
|
|
43
|
+
copyToClipboard() {
|
|
44
|
+
var input = document.createElement('textarea');
|
|
45
|
+
input.innerHTML = this.toCopyCode;
|
|
46
|
+
document.body.appendChild(input);
|
|
47
|
+
input.select();
|
|
48
|
+
var result = document.execCommand('copy');
|
|
49
|
+
document.body.removeChild(input);
|
|
50
|
+
|
|
51
|
+
this.balloonLabel = 'Copied!'
|
|
52
|
+
setTimeout(() => this.balloonLabel = 'Copy', 1500)
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
created() {
|
|
56
|
+
|
|
57
|
+
this.toCopyCode = this.$_label.replace(/`([^`]+)`/g,'$1')
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
</script>
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<vl-form-field v-bind="$_wrapperAttributes">
|
|
3
|
+
<div :class="'vlInputGroup rounded-lg ' + (showError ? invalidClass : '')">
|
|
4
|
+
<input
|
|
5
|
+
v-model="visualValue"
|
|
6
|
+
class="vlFormControl"
|
|
7
|
+
v-bind="$_attributes"
|
|
8
|
+
v-on="$_events"
|
|
9
|
+
placeholder="1234 1234 1234 1234"
|
|
10
|
+
@input="onInputCardNumber"
|
|
11
|
+
@focus="onFocus"
|
|
12
|
+
@blur="focused = false"
|
|
13
|
+
ref="input"
|
|
14
|
+
/>
|
|
15
|
+
<div class="justify-center flex items-center px-4" v-if="showError">
|
|
16
|
+
<span class="invalid-icon">!</span>
|
|
17
|
+
</div>
|
|
18
|
+
</div>
|
|
19
|
+
</vl-form-field>
|
|
20
|
+
</template>
|
|
21
|
+
|
|
22
|
+
<script>
|
|
23
|
+
import Field from 'vue-kompo/js/form/mixins/Field'
|
|
24
|
+
import HasInputAttributes from 'vue-kompo/js/form/mixins/HasInputAttributes'
|
|
25
|
+
|
|
26
|
+
export default {
|
|
27
|
+
mixins: [Field, HasInputAttributes],
|
|
28
|
+
data() {
|
|
29
|
+
return {
|
|
30
|
+
visualValue: this.component?.value || '',
|
|
31
|
+
focused: false,
|
|
32
|
+
dirty: false
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
computed: {
|
|
36
|
+
$_attributes() {
|
|
37
|
+
return {
|
|
38
|
+
...this.$_defaultFieldAttributes,
|
|
39
|
+
...this.$_defaultInputAttributes
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
focusOnLoad(){ return this.$_config('focusOnLoad') },
|
|
43
|
+
invalidClass() { return this.$_config('invalidClass')},
|
|
44
|
+
|
|
45
|
+
rawCardNumbers() { return this.visualValue.replaceAll(/\D/g, '') },
|
|
46
|
+
|
|
47
|
+
formatedCardNumber() {
|
|
48
|
+
const rawNumbers = this.rawCardNumbers;
|
|
49
|
+
|
|
50
|
+
return this.performNumberFormatting(rawNumbers);
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
showError() {
|
|
54
|
+
return !this.isValidCard && !this.focused && this.dirty;
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
isValidCard() {
|
|
58
|
+
const cardNumber = this.rawCardNumbers;
|
|
59
|
+
const amex = /^3[47][0-9]{13}$/;
|
|
60
|
+
const visa = /^4[0-9]{12}(?:[0-9]{3})?$/;
|
|
61
|
+
const mastercard = /^5[1-5][0-9]{14}$/;
|
|
62
|
+
const discover = /^6(?:011|5[0-9]{2})[0-9]{12}$/;
|
|
63
|
+
|
|
64
|
+
return amex.test(cardNumber) || visa.test(cardNumber) || mastercard.test(cardNumber) || discover.test(cardNumber);
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
methods: {
|
|
68
|
+
focus(){
|
|
69
|
+
this.$refs.input.focus()
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
performNumberFormatting(number){
|
|
73
|
+
return number.slice(0, 16).replaceAll(/(.{4})/g, '$1 ').trim();
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
onInputCardNumber() {
|
|
77
|
+
this.visualValue = this.formatedCardNumber;
|
|
78
|
+
this.component.value = this.rawCardNumbers;
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
onFocus() {
|
|
82
|
+
this.focused = true;
|
|
83
|
+
this.dirty = true;
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
mounted(){
|
|
87
|
+
if(this.focusOnLoad)
|
|
88
|
+
this.focus()
|
|
89
|
+
|
|
90
|
+
if(this.component.value) {
|
|
91
|
+
this.visualValue = this.performNumberFormatting(this.component.value)
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
}
|
|
95
|
+
</script>
|
|
96
|
+
|
|
97
|
+
<style scoped>
|
|
98
|
+
.invalid-icon {
|
|
99
|
+
color: red;
|
|
100
|
+
font-weight: bold;
|
|
101
|
+
margin-left: 8px;
|
|
102
|
+
cursor: pointer;
|
|
103
|
+
}
|
|
104
|
+
</style>
|