@edgedev/create-edge-app 1.1.25 → 1.1.26
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 +55 -20
- package/{agent.md → agents.md} +2 -0
- package/bin/cli.js +6 -6
- package/edge/components/auth/login.vue +384 -0
- package/edge/components/auth/register.vue +396 -0
- package/edge/components/auth.vue +108 -0
- package/edge/components/autoFileUpload.vue +215 -0
- package/edge/components/billing.vue +8 -0
- package/edge/components/buttonDivider.vue +14 -0
- package/edge/components/chip.vue +34 -0
- package/edge/components/clipboardButton.vue +42 -0
- package/edge/components/cms/block.vue +529 -0
- package/edge/components/cms/blockApi.vue +212 -0
- package/edge/components/cms/blockEditor.vue +725 -0
- package/edge/components/cms/blockInput.vue +66 -0
- package/edge/components/cms/blockPicker.vue +486 -0
- package/edge/components/cms/blockRender.vue +78 -0
- package/edge/components/cms/blockSheetContent.vue +28 -0
- package/edge/components/cms/codeEditor.vue +466 -0
- package/edge/components/cms/fontUpload.vue +327 -0
- package/edge/components/cms/htmlContent.vue +807 -0
- package/edge/components/cms/init_blocks/api_with_subarrays.html +17 -0
- package/edge/components/cms/init_blocks/array_with_collection.html +7 -0
- package/edge/components/cms/init_blocks/array_with_objects.html +7 -0
- package/edge/components/cms/init_blocks/carousel.html +103 -0
- package/edge/components/cms/init_blocks/contact_us.html +69 -0
- package/edge/components/cms/init_blocks/content_with_left_image.html +27 -0
- package/edge/components/cms/init_blocks/footer.html +24 -0
- package/edge/components/cms/init_blocks/header_divider.html +7 -0
- package/edge/components/cms/init_blocks/hero.html +35 -0
- package/edge/components/cms/init_blocks/hero_carousel.html +52 -0
- package/edge/components/cms/init_blocks/newsletter.html +117 -0
- package/edge/components/cms/init_blocks/post_content.html +7 -0
- package/edge/components/cms/init_blocks/post_title_header.html +21 -0
- package/edge/components/cms/init_blocks/posts_list.html +20 -0
- package/edge/components/cms/init_blocks/properties_showcase.html +100 -0
- package/edge/components/cms/init_blocks/property_carousel.html +59 -0
- package/edge/components/cms/init_blocks/property_detail.html +112 -0
- package/edge/components/cms/init_blocks/property_detail_header.html +34 -0
- package/edge/components/cms/init_blocks/property_results.html +137 -0
- package/edge/components/cms/init_blocks/property_search.html +75 -0
- package/edge/components/cms/init_blocks/simple_array.html +7 -0
- package/edge/components/cms/mediaCard.vue +116 -0
- package/edge/components/cms/mediaManager.vue +386 -0
- package/edge/components/cms/menu.vue +1103 -0
- package/edge/components/cms/optionsSelect.vue +107 -0
- package/edge/components/cms/page.vue +1785 -0
- package/edge/components/cms/posts.vue +1083 -0
- package/edge/components/cms/site.vue +1298 -0
- package/edge/components/cms/themeDefaultMenu.vue +548 -0
- package/edge/components/cms/themeEditor.vue +426 -0
- package/edge/components/dashboard.vue +776 -0
- package/edge/components/editor.vue +671 -0
- package/edge/components/fileTree.vue +72 -0
- package/edge/components/files.vue +89 -0
- package/edge/components/formSubtypes/myOrgs.vue +214 -0
- package/edge/components/formSubtypes/users.vue +336 -0
- package/edge/components/functionChips.vue +57 -0
- package/edge/components/gError.vue +98 -0
- package/edge/components/gHelper.vue +67 -0
- package/edge/components/gInput.vue +1331 -0
- package/edge/components/loggingIn.vue +41 -0
- package/edge/components/menu.vue +137 -0
- package/edge/components/menuContent.vue +132 -0
- package/edge/components/myAccount.vue +317 -0
- package/edge/components/myOrganizations.vue +75 -0
- package/edge/components/myProfile.vue +122 -0
- package/edge/components/orgSwitcher.vue +25 -0
- package/edge/components/organizationMembers.vue +522 -0
- package/edge/components/organizationSettings.vue +271 -0
- package/edge/components/shad/breadcrumbs.vue +35 -0
- package/edge/components/shad/button.vue +43 -0
- package/edge/components/shad/checkbox.vue +73 -0
- package/edge/components/shad/combobox.vue +238 -0
- package/edge/components/shad/datepicker.vue +184 -0
- package/edge/components/shad/dialog.vue +32 -0
- package/edge/components/shad/dropdownMenu.vue +54 -0
- package/edge/components/shad/dropdownMenuItem.vue +21 -0
- package/edge/components/shad/form.vue +59 -0
- package/edge/components/shad/html.vue +877 -0
- package/edge/components/shad/input.vue +139 -0
- package/edge/components/shad/number.vue +109 -0
- package/edge/components/shad/select.vue +151 -0
- package/edge/components/shad/selectTags.vue +278 -0
- package/edge/components/shad/switch.vue +67 -0
- package/edge/components/shad/tags.vue +137 -0
- package/edge/components/shad/textarea.vue +102 -0
- package/edge/components/shad/typeMoney.vue +167 -0
- package/edge/components/sideBar.vue +288 -0
- package/edge/components/sideBarContent.vue +268 -0
- package/edge/components/sidebarProvider.vue +33 -0
- package/edge/components/tooltip.vue +16 -0
- package/edge/components/userMenu.vue +148 -0
- package/edge/components/v/alert.vue +59 -0
- package/edge/components/v/alertTitle.vue +18 -0
- package/edge/components/v/card.vue +53 -0
- package/edge/components/v/cardActions.vue +18 -0
- package/edge/components/v/cardText.vue +18 -0
- package/edge/components/v/cardTitle.vue +20 -0
- package/edge/components/v/col.vue +56 -0
- package/edge/components/v/list.vue +46 -0
- package/edge/components/v/listItem.vue +26 -0
- package/edge/components/v/listItemTitle.vue +18 -0
- package/edge/components/v/row.vue +42 -0
- package/edge/components/v/toolbar.vue +24 -0
- package/edge/composables/global.ts +519 -0
- package/edge-pull.sh +2 -0
- package/edge-push.sh +1 -0
- package/edge-status.sh +14 -0
- package/package.json +1 -1
- package/edge-components-install.sh +0 -1
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { useVModel } from '@vueuse/core'
|
|
3
|
+
import { cn } from '@/lib/utils'
|
|
4
|
+
const props = defineProps({
|
|
5
|
+
name: {
|
|
6
|
+
type: String,
|
|
7
|
+
required: true,
|
|
8
|
+
},
|
|
9
|
+
modelValue: {
|
|
10
|
+
type: Boolean,
|
|
11
|
+
default: false,
|
|
12
|
+
},
|
|
13
|
+
class: {
|
|
14
|
+
type: null,
|
|
15
|
+
required: false,
|
|
16
|
+
},
|
|
17
|
+
switchClass: {
|
|
18
|
+
type: null,
|
|
19
|
+
required: false,
|
|
20
|
+
},
|
|
21
|
+
placeholder: {
|
|
22
|
+
type: String,
|
|
23
|
+
required: false,
|
|
24
|
+
},
|
|
25
|
+
label: {
|
|
26
|
+
type: String,
|
|
27
|
+
required: false,
|
|
28
|
+
},
|
|
29
|
+
description: {
|
|
30
|
+
type: String,
|
|
31
|
+
required: false,
|
|
32
|
+
},
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
const emits = defineEmits(['update:modelValue'])
|
|
36
|
+
|
|
37
|
+
const modelValue = useVModel(props, 'modelValue', emits)
|
|
38
|
+
</script>
|
|
39
|
+
|
|
40
|
+
<template>
|
|
41
|
+
<div>
|
|
42
|
+
<FormField :name="props.name">
|
|
43
|
+
<FormItem :class="cn('flex flex-row items-center justify-between', props.class)">
|
|
44
|
+
<div class="flex flex-row items-center w-full">
|
|
45
|
+
<div class="grow">
|
|
46
|
+
<FormLabel class="text-base">
|
|
47
|
+
{{ props.label }}
|
|
48
|
+
</FormLabel>
|
|
49
|
+
<FormDescription>
|
|
50
|
+
<slot />
|
|
51
|
+
</FormDescription>
|
|
52
|
+
</div>
|
|
53
|
+
<FormControl>
|
|
54
|
+
<Switch
|
|
55
|
+
v-model="modelValue"
|
|
56
|
+
:class="cn('', props.switchClass)"
|
|
57
|
+
/>
|
|
58
|
+
</FormControl>
|
|
59
|
+
</div>
|
|
60
|
+
</FormItem>
|
|
61
|
+
</FormField>
|
|
62
|
+
</div>
|
|
63
|
+
</template>
|
|
64
|
+
|
|
65
|
+
<style lang="scss" scoped>
|
|
66
|
+
|
|
67
|
+
</style>
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { useVModel } from '@vueuse/core'
|
|
3
|
+
import { useField } from 'vee-validate'
|
|
4
|
+
|
|
5
|
+
const props = defineProps({
|
|
6
|
+
name: { type: String, required: true },
|
|
7
|
+
// Accept either String (CSV) or Array depending on valueAs
|
|
8
|
+
modelValue: { type: [String, Array], default: '' },
|
|
9
|
+
class: { type: null, default: '' },
|
|
10
|
+
placeholder: { type: String, default: '' },
|
|
11
|
+
label: { type: String, default: '' },
|
|
12
|
+
description: { type: String, default: '' },
|
|
13
|
+
disabled: { type: Boolean, default: false },
|
|
14
|
+
// NEW: controls how the component reads/emits the value
|
|
15
|
+
valueAs: { type: String, required: false, validator: v => ['string', 'array'].includes(v) },
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
const emit = defineEmits(['update:modelValue', 'blur'])
|
|
19
|
+
|
|
20
|
+
// Determine operational mode: explicit prop wins only when set; otherwise infer from initial modelValue
|
|
21
|
+
const mode = computed(() =>
|
|
22
|
+
(props.valueAs ?? (Array.isArray(props.modelValue) ? 'array' : 'string')),
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
// raw v-model that mirrors props.modelValue directly
|
|
26
|
+
const rawModel = useVModel(props, 'modelValue', emit, {
|
|
27
|
+
passive: false,
|
|
28
|
+
prop: 'modelValue',
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
const normalize = arr =>
|
|
32
|
+
Array.from(new Set((arr || []).map(s => String(s).trim()).filter(Boolean)))
|
|
33
|
+
|
|
34
|
+
const parseCsv = str => normalize(String(str || '').split(','))
|
|
35
|
+
|
|
36
|
+
const { setValue, setTouched } = useField(props.name)
|
|
37
|
+
|
|
38
|
+
// Public array model used by the Tags UI regardless of underlying type
|
|
39
|
+
const arrayModel = computed({
|
|
40
|
+
get: () => {
|
|
41
|
+
if (mode.value === 'array') {
|
|
42
|
+
return normalize(Array.isArray(rawModel.value) ? rawModel.value : [])
|
|
43
|
+
}
|
|
44
|
+
// 'string' mode -> parse CSV
|
|
45
|
+
return parseCsv(rawModel.value)
|
|
46
|
+
},
|
|
47
|
+
set: (arr) => {
|
|
48
|
+
const cleaned = normalize(arr)
|
|
49
|
+
if (mode.value === 'array') {
|
|
50
|
+
rawModel.value = cleaned
|
|
51
|
+
setValue(cleaned, false)
|
|
52
|
+
emit('update:modelValue', cleaned)
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
const csv = cleaned.join(',')
|
|
56
|
+
rawModel.value = csv
|
|
57
|
+
setValue(csv, false)
|
|
58
|
+
emit('update:modelValue', csv)
|
|
59
|
+
}
|
|
60
|
+
setTouched(true)
|
|
61
|
+
},
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
// Keep vee-validate synced with external changes (resets, programmatic updates)
|
|
65
|
+
watch(
|
|
66
|
+
() => rawModel.value,
|
|
67
|
+
(v) => {
|
|
68
|
+
if (mode.value === 'array') {
|
|
69
|
+
setValue(Array.isArray(v) ? v : [], false)
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
setValue(String(v ?? ''), false)
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
{ immediate: true },
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
function commitPendingFromInput(el) {
|
|
79
|
+
if (!el)
|
|
80
|
+
return
|
|
81
|
+
const raw = (el.value || '').trim()
|
|
82
|
+
if (!raw)
|
|
83
|
+
return
|
|
84
|
+
arrayModel.value = [...arrayModel.value, raw]
|
|
85
|
+
el.value = ''
|
|
86
|
+
el.dispatchEvent(new Event('input', { bubbles: true }))
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function onInputBlur(e) {
|
|
90
|
+
commitPendingFromInput(e?.target)
|
|
91
|
+
setTouched(true)
|
|
92
|
+
emit('blur', e)
|
|
93
|
+
}
|
|
94
|
+
</script>
|
|
95
|
+
|
|
96
|
+
<template>
|
|
97
|
+
<FormField :name="props.name">
|
|
98
|
+
<FormItem class="flex flex-col gap-2 p-0 mt-0 space-y-0 [&>*]:!mt-0">
|
|
99
|
+
<div class="flex items-center justify-between">
|
|
100
|
+
<FormLabel v-if="props.label">
|
|
101
|
+
{{ props.label }}
|
|
102
|
+
</FormLabel>
|
|
103
|
+
<slot name="icon" />
|
|
104
|
+
</div>
|
|
105
|
+
<FormControl>
|
|
106
|
+
<TagsInput
|
|
107
|
+
:id="props.name"
|
|
108
|
+
v-model="arrayModel"
|
|
109
|
+
class="mt-0"
|
|
110
|
+
:class="props.class"
|
|
111
|
+
:disabled="props.disabled"
|
|
112
|
+
>
|
|
113
|
+
<TagsInputItem
|
|
114
|
+
v-for="item in arrayModel"
|
|
115
|
+
:key="item"
|
|
116
|
+
:value="item"
|
|
117
|
+
class="bg-secondary"
|
|
118
|
+
>
|
|
119
|
+
<TagsInputItemText />
|
|
120
|
+
<TagsInputItemDelete />
|
|
121
|
+
</TagsInputItem>
|
|
122
|
+
|
|
123
|
+
<!-- Let Enter be handled by the component; commit on blur -->
|
|
124
|
+
<TagsInputInput
|
|
125
|
+
:placeholder="props.placeholder || 'Add…'"
|
|
126
|
+
@blur="onInputBlur"
|
|
127
|
+
/>
|
|
128
|
+
</TagsInput>
|
|
129
|
+
</FormControl>
|
|
130
|
+
|
|
131
|
+
<FormDescription v-if="props.description">
|
|
132
|
+
{{ props.description }}
|
|
133
|
+
</FormDescription>
|
|
134
|
+
<FormMessage />
|
|
135
|
+
</FormItem>
|
|
136
|
+
</FormField>
|
|
137
|
+
</template>
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { useVModel } from '@vueuse/core'
|
|
3
|
+
const props = defineProps({
|
|
4
|
+
name: {
|
|
5
|
+
type: String,
|
|
6
|
+
required: true,
|
|
7
|
+
},
|
|
8
|
+
defaultValue: {
|
|
9
|
+
type: [String, Number],
|
|
10
|
+
required: false,
|
|
11
|
+
},
|
|
12
|
+
modelValue: {
|
|
13
|
+
type: [String, Number],
|
|
14
|
+
required: false,
|
|
15
|
+
},
|
|
16
|
+
class: {
|
|
17
|
+
type: null,
|
|
18
|
+
required: false,
|
|
19
|
+
},
|
|
20
|
+
placeholder: {
|
|
21
|
+
type: String,
|
|
22
|
+
required: false,
|
|
23
|
+
},
|
|
24
|
+
label: {
|
|
25
|
+
type: String,
|
|
26
|
+
required: false,
|
|
27
|
+
},
|
|
28
|
+
description: {
|
|
29
|
+
type: String,
|
|
30
|
+
required: false,
|
|
31
|
+
},
|
|
32
|
+
maskOptions: {
|
|
33
|
+
type: [Object],
|
|
34
|
+
required: false,
|
|
35
|
+
default: null,
|
|
36
|
+
},
|
|
37
|
+
disabled: {
|
|
38
|
+
type: Boolean,
|
|
39
|
+
required: false,
|
|
40
|
+
default: false,
|
|
41
|
+
},
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
const emits = defineEmits(['update:modelValue'])
|
|
45
|
+
|
|
46
|
+
const state = reactive({
|
|
47
|
+
showPassword: false,
|
|
48
|
+
type: '',
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
onBeforeMount(() => {
|
|
52
|
+
state.type = props.type
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
const modelValue = useVModel(props, 'modelValue', emits, {
|
|
56
|
+
passive: true,
|
|
57
|
+
defaultValue: props.defaultValue,
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
const classComputed = computed(() => {
|
|
61
|
+
return props.class
|
|
62
|
+
})
|
|
63
|
+
</script>
|
|
64
|
+
|
|
65
|
+
<template>
|
|
66
|
+
<div>
|
|
67
|
+
<FormField v-slot="{ componentField }" :name="props.name">
|
|
68
|
+
<FormItem>
|
|
69
|
+
<FormLabel>
|
|
70
|
+
{{ props.label }}
|
|
71
|
+
<div class="ml-auto inline-block">
|
|
72
|
+
<slot />
|
|
73
|
+
</div>
|
|
74
|
+
</FormLabel>
|
|
75
|
+
<FormControl>
|
|
76
|
+
<div class="relative w-full items-center">
|
|
77
|
+
<Textarea
|
|
78
|
+
v-model="modelValue"
|
|
79
|
+
:class="classComputed"
|
|
80
|
+
:placeholder="props.placeholder"
|
|
81
|
+
:disabled="props.disabled"
|
|
82
|
+
:default-value="modelValue"
|
|
83
|
+
v-bind="componentField"
|
|
84
|
+
/>
|
|
85
|
+
<span class="absolute end-0 inset-y-0 flex items-center justify-center px-2">
|
|
86
|
+
<slot name="icon" />
|
|
87
|
+
</span>
|
|
88
|
+
</div>
|
|
89
|
+
</FormControl>
|
|
90
|
+
<FormDescription>
|
|
91
|
+
{{ props.description }}
|
|
92
|
+
<slot name="description" />
|
|
93
|
+
</FormDescription>
|
|
94
|
+
<FormMessage />
|
|
95
|
+
</FormItem>
|
|
96
|
+
</FormField>
|
|
97
|
+
</div>
|
|
98
|
+
</template>
|
|
99
|
+
|
|
100
|
+
<style lang="scss" scoped>
|
|
101
|
+
|
|
102
|
+
</style>
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { useVModel } from '@vueuse/core'
|
|
3
|
+
|
|
4
|
+
const props = defineProps({
|
|
5
|
+
name: { type: String, required: true },
|
|
6
|
+
type: { type: String, default: 'text' },
|
|
7
|
+
defaultValue: { type: [String, Number], required: false },
|
|
8
|
+
class: { type: null, required: false },
|
|
9
|
+
placeholder: { type: String, required: false },
|
|
10
|
+
label: { type: String, required: false },
|
|
11
|
+
description: { type: String, required: false },
|
|
12
|
+
maskOptions: { type: [Object], default: null },
|
|
13
|
+
disabled: { type: Boolean, default: false },
|
|
14
|
+
modelValue: { type: [String, Number], default: '' },
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
const emits = defineEmits(['update:modelValue'])
|
|
18
|
+
|
|
19
|
+
const state = reactive({
|
|
20
|
+
showPassword: false,
|
|
21
|
+
type: '',
|
|
22
|
+
editMode: false,
|
|
23
|
+
isDebit: false, // false = credit, true = debit
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
onBeforeMount(() => {
|
|
27
|
+
state.type = props.type
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
// Initialize useVModel
|
|
31
|
+
const modelValue = useVModel(props, 'modelValue', emits, {
|
|
32
|
+
prop: 'modelValue',
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
watch(modelValue, (val) => {
|
|
36
|
+
const stringValue = String(val || '')
|
|
37
|
+
const newVal = parseFloat(stringValue.replace(/[$,]/g, ''))
|
|
38
|
+
if (isNaN(newVal)) {
|
|
39
|
+
modelValue.value = 0
|
|
40
|
+
return
|
|
41
|
+
}
|
|
42
|
+
emits('update:modelValue', state.isDebit ? -Math.abs(newVal) : Math.abs(newVal))
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
const toggleDebit = () => {
|
|
46
|
+
state.isDebit = !state.isDebit
|
|
47
|
+
if (modelValue.value !== '') {
|
|
48
|
+
modelValue.value = state.isDebit
|
|
49
|
+
? -Math.abs(modelValue.value)
|
|
50
|
+
: Math.abs(modelValue.value)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const classComputed = computed(() => {
|
|
55
|
+
return props.type === 'password' ? `${props.class} pr-10` : props.class
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
const handleKeydown = (event) => {
|
|
59
|
+
const allowedKeys = [
|
|
60
|
+
'Backspace',
|
|
61
|
+
'ArrowLeft',
|
|
62
|
+
'ArrowRight',
|
|
63
|
+
'Delete',
|
|
64
|
+
'Tab',
|
|
65
|
+
]
|
|
66
|
+
const key = event.key
|
|
67
|
+
const value = event.target.value
|
|
68
|
+
const selectionStart = event.target.selectionStart
|
|
69
|
+
|
|
70
|
+
if (
|
|
71
|
+
allowedKeys.includes(key)
|
|
72
|
+
|| (key >= '0' && key <= '9')
|
|
73
|
+
|| (key === '.' && !value.includes('.'))
|
|
74
|
+
) {
|
|
75
|
+
if (value.includes('.') && key >= '0' && key <= '9') {
|
|
76
|
+
const decimalPart = value.split('.')[1]
|
|
77
|
+
if (
|
|
78
|
+
decimalPart.length >= 2
|
|
79
|
+
&& selectionStart > value.indexOf('.')
|
|
80
|
+
) {
|
|
81
|
+
event.preventDefault()
|
|
82
|
+
return
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Disallow minus and any other invalid input
|
|
89
|
+
event.preventDefault()
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const moneyMask = {
|
|
93
|
+
preProcess: val => val.replace(/[$,]/g, ''),
|
|
94
|
+
postProcess: (val) => {
|
|
95
|
+
if (!val)
|
|
96
|
+
return ''
|
|
97
|
+
const sub = 3 - (val.includes('.') ? val.length - val.indexOf('.') : 0)
|
|
98
|
+
return Intl.NumberFormat('en-US', {
|
|
99
|
+
style: 'currency',
|
|
100
|
+
currency: 'USD',
|
|
101
|
+
}).format(val).slice(0, sub ? -sub : undefined)
|
|
102
|
+
},
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const formatDecimal = (event) => {
|
|
106
|
+
const el = event.target
|
|
107
|
+
const cleaned = el.value.replace(/[$,]/g, '')
|
|
108
|
+
const num = parseFloat(cleaned)
|
|
109
|
+
|
|
110
|
+
if (!isNaN(num)) {
|
|
111
|
+
const fixed = num.toFixed(2)
|
|
112
|
+
modelValue.value = state.isDebit
|
|
113
|
+
? -Math.abs(parseFloat(fixed))
|
|
114
|
+
: Math.abs(parseFloat(fixed))
|
|
115
|
+
|
|
116
|
+
// Force input's displayed value to update
|
|
117
|
+
el.value = fixed
|
|
118
|
+
el.dispatchEvent(new Event('input'))
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
</script>
|
|
122
|
+
|
|
123
|
+
<template>
|
|
124
|
+
<div>
|
|
125
|
+
<FormField v-slot="{ componentField }" :name="props.name">
|
|
126
|
+
<FormItem>
|
|
127
|
+
<FormLabel class="flex">
|
|
128
|
+
{{ props.label }}
|
|
129
|
+
<div class="ml-auto inline-block">
|
|
130
|
+
<slot />
|
|
131
|
+
</div>
|
|
132
|
+
</FormLabel>
|
|
133
|
+
|
|
134
|
+
<div class="flex items-center mb-2">
|
|
135
|
+
<button
|
|
136
|
+
type="button"
|
|
137
|
+
class="border px-2 py-2 rounded text-sm w-[100px] bg-primary text-primary-foreground"
|
|
138
|
+
@click="toggleDebit"
|
|
139
|
+
>
|
|
140
|
+
{{ state.isDebit ? 'Debit (−)' : 'Credit (+)' }}
|
|
141
|
+
</button>
|
|
142
|
+
<FormControl>
|
|
143
|
+
<Input
|
|
144
|
+
:id="props.name"
|
|
145
|
+
v-model="modelValue"
|
|
146
|
+
v-maska:[moneyMask]
|
|
147
|
+
:default-value="props.modelValue"
|
|
148
|
+
:class="classComputed"
|
|
149
|
+
:type="state.type"
|
|
150
|
+
v-bind="componentField"
|
|
151
|
+
:placeholder="props.placeholder"
|
|
152
|
+
:disabled="props.disabled"
|
|
153
|
+
@keydown="handleKeydown"
|
|
154
|
+
@blur="formatDecimal"
|
|
155
|
+
/>
|
|
156
|
+
</FormControl>
|
|
157
|
+
</div>
|
|
158
|
+
|
|
159
|
+
<FormDescription>
|
|
160
|
+
{{ props.description }}
|
|
161
|
+
<slot name="description" />
|
|
162
|
+
</FormDescription>
|
|
163
|
+
<FormMessage />
|
|
164
|
+
</FormItem>
|
|
165
|
+
</FormField>
|
|
166
|
+
</div>
|
|
167
|
+
</template>
|