pgo-ui 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 +195 -0
- package/dist/InputSearch-CHSoQ7GH.js +155 -0
- package/dist/favicon.ico +0 -0
- package/dist/index-B7ko30VS.js +5899 -0
- package/dist/index-BKsLeoKP.js +4781 -0
- package/dist/index-BeW0KHDT.js +34237 -0
- package/dist/index-DjwGqWIf.js +5086 -0
- package/dist/index-jnIKSPXM.js +4949 -0
- package/dist/index.es.js +58 -0
- package/dist/index.umd.js +111 -0
- package/dist/pgo-ui.css +1 -0
- package/package.json +99 -0
- package/src/App.vue +369 -0
- package/src/assets/fonts/Faruma.ttf +0 -0
- package/src/assets/logo.png +0 -0
- package/src/components/examples/AppBarExample.vue +100 -0
- package/src/components/examples/AvatarExample.vue +47 -0
- package/src/components/examples/BannerExample.vue +287 -0
- package/src/components/examples/BaseInputExample.vue +25 -0
- package/src/components/examples/BreadcrumbExample.vue +53 -0
- package/src/components/examples/CardExample.vue +77 -0
- package/src/components/examples/ChipExample.vue +225 -0
- package/src/components/examples/DatePickerExample.vue +31 -0
- package/src/components/examples/DropdownExample.vue +84 -0
- package/src/components/examples/EditorExample.vue +200 -0
- package/src/components/examples/ExpansionPanelExample.vue +42 -0
- package/src/components/examples/FileUploadExample.vue +40 -0
- package/src/components/examples/FormExample.vue +121 -0
- package/src/components/examples/HugeTest.vue +8 -0
- package/src/components/examples/LayoutContainerExample.vue +80 -0
- package/src/components/examples/ModalExample.vue +82 -0
- package/src/components/examples/NavDrawerExample.vue +171 -0
- package/src/components/examples/NumberFieldExample.vue +145 -0
- package/src/components/examples/RadioButtonExample.vue +161 -0
- package/src/components/examples/SearchExample.vue +322 -0
- package/src/components/examples/SelectExample.vue +121 -0
- package/src/components/examples/StackedTableViewExample.vue +53 -0
- package/src/components/examples/TabExample.vue +336 -0
- package/src/components/examples/TableExample.vue +228 -0
- package/src/components/examples/TextFieldExample.vue +181 -0
- package/src/components/examples/TextareaExample.vue +173 -0
- package/src/components/examples/ThemeToggle.vue +50 -0
- package/src/components/examples/TimelineExample.vue +66 -0
- package/src/components/examples/TipTapEditorExample.vue +20 -0
- package/src/components/examples/TooltipExample.vue +53 -0
- package/src/components/examples/VueDatePickerShowcase.vue +214 -0
- package/src/components/examples/_DatePickerExample.vue +33 -0
- package/src/components/examples/__FormExample.vue +77 -0
- package/src/components/index.ts +25 -0
- package/src/components/pgo/AppBar.vue +348 -0
- package/src/components/pgo/Avatar.vue +139 -0
- package/src/components/pgo/Banner.vue +300 -0
- package/src/components/pgo/Breadcrumb.vue +103 -0
- package/src/components/pgo/Button.vue +171 -0
- package/src/components/pgo/Card.vue +179 -0
- package/src/components/pgo/ConfirmationModel.vue +32 -0
- package/src/components/pgo/DataTable.vue +845 -0
- package/src/components/pgo/DatePicker/CalendarPanel.vue +43 -0
- package/src/components/pgo/DatePicker/__DatePicker.vue +122 -0
- package/src/components/pgo/DatePicker/types.ts +11 -0
- package/src/components/pgo/DatePicker/useCalendar.ts +39 -0
- package/src/components/pgo/DatePicker/useDatePicker.ts +31 -0
- package/src/components/pgo/Deprecated/ToastContainer.vue +51 -0
- package/src/components/pgo/Deprecated/ToastItem.vue +55 -0
- package/src/components/pgo/Dropdown.vue +296 -0
- package/src/components/pgo/DropdownItem.vue +40 -0
- package/src/components/pgo/Editor.vue +511 -0
- package/src/components/pgo/ExpansionPanel.vue +185 -0
- package/src/components/pgo/Footer.vue +39 -0
- package/src/components/pgo/HeroIcon.vue +124 -0
- package/src/components/pgo/LayoutContainer.vue +104 -0
- package/src/components/pgo/Main.vue +37 -0
- package/src/components/pgo/Modal.vue +261 -0
- package/src/components/pgo/NavDrawer.vue +127 -0
- package/src/components/pgo/NavDrawerItem.vue +161 -0
- package/src/components/pgo/NavigationDrawer.vue +850 -0
- package/src/components/pgo/OLDNavDrawer.vue +661 -0
- package/src/components/pgo/OldAppBar.vue +223 -0
- package/src/components/pgo/PApp.vue +102 -0
- package/src/components/pgo/Pagination.vue +242 -0
- package/src/components/pgo/Search copy.vue +310 -0
- package/src/components/pgo/Search.vue +411 -0
- package/src/components/pgo/StackedTableView.vue +167 -0
- package/src/components/pgo/Tab.vue +617 -0
- package/src/components/pgo/TestInput.vue +395 -0
- package/src/components/pgo/Timeline.vue +367 -0
- package/src/components/pgo/TimelineItem.vue +80 -0
- package/src/components/pgo/TipTapEditor.vue +315 -0
- package/src/components/pgo/Tooltip.NOTES.md +12 -0
- package/src/components/pgo/Tooltip.PROPS.md +21 -0
- package/src/components/pgo/Tooltip.vue +281 -0
- package/src/components/pgo/base/Base.vue +444 -0
- package/src/components/pgo/buttons/Chip.vue +324 -0
- package/src/components/pgo/buttons/ChipGroup.vue +224 -0
- package/src/components/pgo/buttons/Radio.vue +424 -0
- package/src/components/pgo/filters/FilterSection.vue +188 -0
- package/src/components/pgo/filters/Searchbar.vue +216 -0
- package/src/components/pgo/forms/DynamicForm.vue +45 -0
- package/src/components/pgo/forms/Form.vue +132 -0
- package/src/components/pgo/index.ts +89 -0
- package/src/components/pgo/inputs/Checkbox.vue +320 -0
- package/src/components/pgo/inputs/DatePicker.vue +395 -0
- package/src/components/pgo/inputs/FileUpload.vue +326 -0
- package/src/components/pgo/inputs/InputSearch.vue +194 -0
- package/src/components/pgo/inputs/NumberField.vue +243 -0
- package/src/components/pgo/inputs/Radio.vue +162 -0
- package/src/components/pgo/inputs/RadioGroup.vue +188 -0
- package/src/components/pgo/inputs/Select.vue +535 -0
- package/src/components/pgo/inputs/TextField.vue +194 -0
- package/src/components/pgo/inputs/Textarea.vue +181 -0
- package/src/index.js +81 -0
- package/src/main.js +12 -0
- package/src/pgo-components/__index.js +104 -0
- package/src/pgo-components/_index.js +31 -0
- package/src/pgo-components/assets/fonts/Faruma.ttf +0 -0
- package/src/pgo-components/assets/fonts/logo.png +0 -0
- package/src/pgo-components/composables/useTheme.js +10 -0
- package/src/pgo-components/directives/tooltip-directive.ts +393 -0
- package/src/pgo-components/lib/componentConfig.js +147 -0
- package/src/pgo-components/lib/core/composables/_useCalendar.ts +127 -0
- package/src/pgo-components/lib/core/composables/useDefaults.ts +15 -0
- package/src/pgo-components/lib/core/composables/useLanguageSelect.js +0 -0
- package/src/pgo-components/lib/core/composables/useRtl.ts +12 -0
- package/src/pgo-components/lib/core/defaults/createDefaults.ts +5 -0
- package/src/pgo-components/lib/core/defaults/defaults.ts +7 -0
- package/src/pgo-components/lib/core/rtl/rtl.ts +3 -0
- package/src/pgo-components/lib/core/rtl/setRtl.ts +19 -0
- package/src/pgo-components/lib/drawerState.ts +3 -0
- package/src/pgo-components/lib/i18n/defaultLables.js +71 -0
- package/src/pgo-components/lib/i18n/i18nPlugin.js +52 -0
- package/src/pgo-components/lib/i18n/useI18n.js +35 -0
- package/src/pgo-components/lib/index.ts +38 -0
- package/src/pgo-components/pages/Component.vue +7 -0
- package/src/pgo-components/pages/ComponentRenderer.vue +99 -0
- package/src/pgo-components/pages/Home.vue +125 -0
- package/src/pgo-components/pages/ListView.vue +372 -0
- package/src/pgo-components/pages/Page1.vue +296 -0
- package/src/pgo-components/pages/_Page1.vue +180 -0
- package/src/pgo-components/plugins/SnackBar.vue +251 -0
- package/src/pgo-components/plugins/SnackBarContainer.vue +53 -0
- package/src/pgo-components/plugins/SnackBarPlugin.ts +136 -0
- package/src/pgo-components/plugins/theme-plugin.js +114 -0
- package/src/pgo-components/plugins/types.ts +46 -0
- package/src/pgo-components/plugins/useSnackBar.js +11 -0
- package/src/pgo-components/plugins/useSnackBar.ts +21 -0
- package/src/pgo-components/plugins/validation-plugin.js +11 -0
- package/src/pgo-components/services/Entry.json +813 -0
- package/src/pgo-components/services/axios.js +54 -0
- package/src/pgo-components/services/data.json +90 -0
- package/src/pgo-components/services/person.json +260 -0
- package/src/pgo-components/services/toast.ts +44 -0
- package/src/pgo-components/styles/global.css +234 -0
- package/src/pgo-components/styles/reset.css +96 -0
- package/src/pgo-components/styles/tokens.css +18 -0
- package/src/pgo-components/styles/utilities/border-radius.css +57 -0
- package/src/pgo-components/styles/utilities/borders.css +85 -0
- package/src/pgo-components/styles/utilities/colors.css +38 -0
- package/src/pgo-components/styles/utilities/cursor.css +19 -0
- package/src/pgo-components/styles/utilities/display.css +78 -0
- package/src/pgo-components/styles/utilities/elevation.css +33 -0
- package/src/pgo-components/styles/utilities/flex.css +403 -0
- package/src/pgo-components/styles/utilities/float.css +41 -0
- package/src/pgo-components/styles/utilities/hover.css +9 -0
- package/src/pgo-components/styles/utilities/index.css +18 -0
- package/src/pgo-components/styles/utilities/opacity.css +27 -0
- package/src/pgo-components/styles/utilities/overflow.css +26 -0
- package/src/pgo-components/styles/utilities/palette.css +515 -0
- package/src/pgo-components/styles/utilities/position.css +14 -0
- package/src/pgo-components/styles/utilities/sizing.css +70 -0
- package/src/pgo-components/styles/utilities/spacing.css +578 -0
- package/src/pgo-components/styles/utilities/transitions.css +58 -0
- package/src/pgo-components/styles/utilities/typography.css +91 -0
- package/src/pgo-components/styles/utilities/z-index.css +11 -0
- package/src/pgo-components/tokens/index.js +337 -0
- package/src/router/index.js +88 -0
- package/src/shims-vue.d.ts +14 -0
- package/src/validations/validationRules.js +50 -0
- package/tailwind.config.js +73 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<Card
|
|
3
|
+
v-if="modelValue"
|
|
4
|
+
:model-value="modelValue"
|
|
5
|
+
:bg="''"
|
|
6
|
+
:border="''"
|
|
7
|
+
:margin="'m-0'"
|
|
8
|
+
:padding="'py-0 px-2'"
|
|
9
|
+
@update:modelValue="(val) => emit('update:modelValue', val)"
|
|
10
|
+
>
|
|
11
|
+
<div class="w-full flex gap-4 justify-between items-center">
|
|
12
|
+
<!-- searchbar title -->
|
|
13
|
+
<div class="flex gap-2 font-semibold text-lg">
|
|
14
|
+
<h1>{{ title }}</h1>
|
|
15
|
+
</div>
|
|
16
|
+
<div class="flex gap-2 items-center">
|
|
17
|
+
<!-- create btn -->
|
|
18
|
+
<Button
|
|
19
|
+
v-if="createButton"
|
|
20
|
+
label="Create"
|
|
21
|
+
color="primary"
|
|
22
|
+
prepend-icon="plus"
|
|
23
|
+
variant="outlined"
|
|
24
|
+
size="xs"
|
|
25
|
+
class="stroke-2"
|
|
26
|
+
@click="$emit('createButton')"
|
|
27
|
+
/>
|
|
28
|
+
|
|
29
|
+
<!-- Dynamic searchbar filters -->
|
|
30
|
+
<div class="flex gap-2">
|
|
31
|
+
<component
|
|
32
|
+
v-for="(field, index) in quickFilters"
|
|
33
|
+
:key="field.key || index"
|
|
34
|
+
:is="getFieldComponent(field.type)"
|
|
35
|
+
:model-value="quickFilterValues[field.key]"
|
|
36
|
+
v-bind="getFieldProps(field)"
|
|
37
|
+
@update:model-value="updateQuickFilter(field.key, $event)"
|
|
38
|
+
/>
|
|
39
|
+
</div>
|
|
40
|
+
|
|
41
|
+
<!-- FilterSection component related -->
|
|
42
|
+
<div v-if="filterSection" class="flex gap-2">
|
|
43
|
+
<!-- for filterSection selected filter count -->
|
|
44
|
+
<Chip
|
|
45
|
+
v-if="filterCountMessage"
|
|
46
|
+
:label="filterCountMessage"
|
|
47
|
+
variant="outlined"
|
|
48
|
+
color="gray"
|
|
49
|
+
size="small"
|
|
50
|
+
:closable="false"
|
|
51
|
+
:disabled="true"
|
|
52
|
+
/>
|
|
53
|
+
<!-- show filterSection -->
|
|
54
|
+
<Button
|
|
55
|
+
label=""
|
|
56
|
+
v-tooltip="showFilterSection ? 'Hide Filters' : 'Show Filters'"
|
|
57
|
+
color="secondary"
|
|
58
|
+
icon="funnel"
|
|
59
|
+
variant="text"
|
|
60
|
+
size="md"
|
|
61
|
+
@click="handlefilterSectionToggle"
|
|
62
|
+
/>
|
|
63
|
+
</div>
|
|
64
|
+
<div class="flex gap-2">
|
|
65
|
+
<Button
|
|
66
|
+
v-tooltip="'Refresh'"
|
|
67
|
+
label=""
|
|
68
|
+
color="gray"
|
|
69
|
+
icon="arrow-path"
|
|
70
|
+
variant="text"
|
|
71
|
+
size="md"
|
|
72
|
+
@click="$emit('refresh')"
|
|
73
|
+
/>
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
</Card>
|
|
78
|
+
</template>
|
|
79
|
+
|
|
80
|
+
<script setup>
|
|
81
|
+
import { ref, computed, defineAsyncComponent } from 'vue'
|
|
82
|
+
import Banner from '../Banner.vue'
|
|
83
|
+
// import Button from '../Button.vue'
|
|
84
|
+
// import HeroIcon from '../HeroIcon.vue'
|
|
85
|
+
import Chip from '../../../'
|
|
86
|
+
// import Card from '../Card.vue'
|
|
87
|
+
import { margins } from '../../../pgo-components/lib/componentConfig'
|
|
88
|
+
|
|
89
|
+
const props = defineProps({
|
|
90
|
+
modelValue: { type: Boolean, default: true },
|
|
91
|
+
title: { type: String, default: '' },
|
|
92
|
+
margin: { type: String, default: '' },
|
|
93
|
+
bg: { type: String, default: 'primary' },
|
|
94
|
+
position: { type: String, default: 'top' },
|
|
95
|
+
border: { type: String, default: '' },
|
|
96
|
+
closeable: { type: Boolean, default: false },
|
|
97
|
+
sticky: { type: Boolean, default: false },
|
|
98
|
+
timeout: { type: Number, default: 0 },
|
|
99
|
+
showProgress: { type: Boolean, default: false },
|
|
100
|
+
rounded: { type: String, default: 'none' },
|
|
101
|
+
elevation: { type: Boolean, default: true },
|
|
102
|
+
outlined: { type: Boolean, default: false },
|
|
103
|
+
dense: { type: Boolean, default: true },
|
|
104
|
+
filterSection: { type: Boolean, default: true },
|
|
105
|
+
createButton: { type: Boolean, default: true },
|
|
106
|
+
quickFilters: {
|
|
107
|
+
type: Array,
|
|
108
|
+
default: () => [] // Quick access filters shown in searchbar
|
|
109
|
+
},
|
|
110
|
+
filterValues: {
|
|
111
|
+
type: Object,
|
|
112
|
+
default: () => ({}) // Values for quick filters
|
|
113
|
+
}
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
const emit = defineEmits(['update:modelValue', 'update:filterValues', 'createButton', 'close', 'dismiss', 'showFilterSection', 'search', 'refresh'])
|
|
117
|
+
|
|
118
|
+
const showFilterSection = ref(false)
|
|
119
|
+
const handlefilterSectionToggle = () => {
|
|
120
|
+
showFilterSection.value = !showFilterSection.value
|
|
121
|
+
emit('showFilterSection', showFilterSection.value)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const quickFilterValues = computed({
|
|
125
|
+
get: () => props.filterValues,
|
|
126
|
+
set: (val) => emit('update:filterValues', val)
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
// Dynamic component map - only imports components that are used
|
|
130
|
+
const componentMap = computed(() => {
|
|
131
|
+
const map = {}
|
|
132
|
+
const usedTypes = new Set(props.quickFilters.map(f => f.type?.toLowerCase()))
|
|
133
|
+
|
|
134
|
+
if (usedTypes.has('search') || usedTypes.has('inputsearch')) {
|
|
135
|
+
map['search'] = defineAsyncComponent(() => import('../inputs/InputSearch.vue'))
|
|
136
|
+
map['inputsearch'] = defineAsyncComponent(() => import('../inputs/InputSearch.vue'))
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (usedTypes.has('select')) {
|
|
140
|
+
map['select'] = defineAsyncComponent(() => import('../inputs/Select.vue'))
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (usedTypes.has('textfield') || usedTypes.has('text')) {
|
|
144
|
+
map['textfield'] = defineAsyncComponent(() => import('../inputs/TextField.vue'))
|
|
145
|
+
map['text'] = defineAsyncComponent(() => import('../inputs/TextField.vue'))
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (usedTypes.has('datepicker') || usedTypes.has('date')) {
|
|
149
|
+
map['datepicker'] = defineAsyncComponent(() => import('../inputs/DatePicker.vue'))
|
|
150
|
+
map['date'] = defineAsyncComponent(() => import('../inputs/DatePicker.vue'))
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (usedTypes.has('chipgroup')) {
|
|
154
|
+
map['chipgroup'] = defineAsyncComponent(() => import('../buttons/ChipGroup.vue'))
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return map
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
const getFieldComponent = (type) => {
|
|
161
|
+
const normalizedType = type?.toLowerCase() || 'search'
|
|
162
|
+
return componentMap.value[normalizedType] || componentMap.value['search']
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const getFieldProps = (field) => {
|
|
166
|
+
const { type, key, ...restProps } = field
|
|
167
|
+
|
|
168
|
+
// Remove empty values to allow defaults to work
|
|
169
|
+
const cleanProps = Object.fromEntries(
|
|
170
|
+
Object.entries(restProps).filter(([_, value]) => value !== '' && value !== null && value !== undefined)
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
// Convert camelCase props to kebab-case
|
|
174
|
+
if (cleanProps.itemText) {
|
|
175
|
+
cleanProps['item-text'] = cleanProps.itemText
|
|
176
|
+
delete cleanProps.itemText
|
|
177
|
+
}
|
|
178
|
+
if (cleanProps.itemValue) {
|
|
179
|
+
cleanProps['item-value'] = cleanProps.itemValue
|
|
180
|
+
delete cleanProps.itemValue
|
|
181
|
+
}
|
|
182
|
+
if (cleanProps.itemTitle) {
|
|
183
|
+
cleanProps['item-title'] = cleanProps.itemTitle
|
|
184
|
+
delete cleanProps.itemTitle
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return cleanProps
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const updateQuickFilter = (key, value) => {
|
|
191
|
+
const normalizedValue = typeof value === 'object' && value !== null ? value.value : value
|
|
192
|
+
|
|
193
|
+
emit('update:filterValues', {
|
|
194
|
+
...props.filterValues,
|
|
195
|
+
[key]: normalizedValue
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
// Auto-trigger search on quick filter change
|
|
199
|
+
emit('search', {
|
|
200
|
+
...props.filterValues,
|
|
201
|
+
[key]: normalizedValue
|
|
202
|
+
})
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Count active filters
|
|
206
|
+
const filterCountMessage = computed(() => {
|
|
207
|
+
if (props.message) return props.message
|
|
208
|
+
|
|
209
|
+
const count = Object.values(props.filterValues).filter(val => {
|
|
210
|
+
if (Array.isArray(val)) return val.length > 0
|
|
211
|
+
return val !== '' && val !== null && val !== undefined
|
|
212
|
+
}).length
|
|
213
|
+
|
|
214
|
+
return count > 0 ? `${count} filter${count > 1 ? 's' : ''} selected` : ''
|
|
215
|
+
})
|
|
216
|
+
</script>
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<Modal v-model="open" persistent>
|
|
3
|
+
<template #header>
|
|
4
|
+
<div>
|
|
5
|
+
<h1 class="text-lg font-semibold" > create</h1>
|
|
6
|
+
</div>
|
|
7
|
+
</template>
|
|
8
|
+
<Form
|
|
9
|
+
v-model="valid"
|
|
10
|
+
>
|
|
11
|
+
|
|
12
|
+
</Form>
|
|
13
|
+
<template #footer>
|
|
14
|
+
<div class="flex justify-end">
|
|
15
|
+
<Button label="Close" color="primary" @click="open = false"/>
|
|
16
|
+
</div>
|
|
17
|
+
</template>
|
|
18
|
+
</Modal>
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
</template>
|
|
23
|
+
<script setup >
|
|
24
|
+
import { ref, computed, watch } from 'vue'
|
|
25
|
+
import Form from './Form.vue';
|
|
26
|
+
import Modal from '../Modal.vue'
|
|
27
|
+
import Button from '../Button.vue'
|
|
28
|
+
|
|
29
|
+
const valid = ref(true);
|
|
30
|
+
const open = ref(true);
|
|
31
|
+
|
|
32
|
+
const emit = defineEmits(['update:modelValue']);
|
|
33
|
+
const props = defineProps({
|
|
34
|
+
modelValue: { type: Boolean, default: false },
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
watch(() => props.modelValue, (newVal) => {
|
|
38
|
+
open.value = newVal;
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
watch(open, (newVal) => {
|
|
42
|
+
emit('update:modelValue', newVal);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
</script>
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<form @submit.prevent="handleSubmit">
|
|
3
|
+
<slot
|
|
4
|
+
:isValid="isValid"
|
|
5
|
+
:validate="validate"
|
|
6
|
+
:reset="reset"
|
|
7
|
+
:resetValidation="resetValidation"
|
|
8
|
+
/>
|
|
9
|
+
</form>
|
|
10
|
+
</template>
|
|
11
|
+
|
|
12
|
+
<script setup>
|
|
13
|
+
import { reactive, ref, provide, computed, nextTick } from 'vue'
|
|
14
|
+
|
|
15
|
+
const props = defineProps({
|
|
16
|
+
modelValue: { type: Boolean, default: null },
|
|
17
|
+
validateOn: {
|
|
18
|
+
type: String,
|
|
19
|
+
default: 'blur'
|
|
20
|
+
}
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
const emit = defineEmits(['update:modelValue', 'submit'])
|
|
24
|
+
|
|
25
|
+
const fields = reactive(new Map())
|
|
26
|
+
const errors = reactive({})
|
|
27
|
+
const hasValidated = reactive({}) // Track which fields have been validated
|
|
28
|
+
|
|
29
|
+
const register = (uid, field) => {
|
|
30
|
+
fields.set(uid, field)
|
|
31
|
+
// Mark as not validated initially
|
|
32
|
+
hasValidated[uid] = false
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const unregister = (uid) => {
|
|
36
|
+
fields.delete(uid)
|
|
37
|
+
delete errors[uid]
|
|
38
|
+
delete hasValidated[uid]
|
|
39
|
+
updateModelValue()
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const validate = async () => {
|
|
43
|
+
let allValid = true
|
|
44
|
+
Object.keys(errors).forEach(k => delete errors[k])
|
|
45
|
+
|
|
46
|
+
for (const [uid, field] of fields.entries()) {
|
|
47
|
+
if (field?.validate) {
|
|
48
|
+
hasValidated[uid] = true
|
|
49
|
+
const valid = await field.validate()
|
|
50
|
+
if (!valid) {
|
|
51
|
+
allValid = false
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
updateModelValue()
|
|
57
|
+
return { valid: allValid, errors: { ...errors } }
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const reset = () => {
|
|
61
|
+
for (const [uid, field] of fields.entries()) {
|
|
62
|
+
if (field?.reset) {
|
|
63
|
+
field.reset()
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
resetValidation()
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const resetValidation = () => {
|
|
70
|
+
for (const [uid, field] of fields.entries()) {
|
|
71
|
+
if (field?.resetValidation) {
|
|
72
|
+
field.resetValidation()
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
Object.keys(errors).forEach(k => delete errors[k])
|
|
76
|
+
Object.keys(hasValidated).forEach(k => hasValidated[k] = false)
|
|
77
|
+
updateModelValue()
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Called by Base input when validating
|
|
82
|
+
*/
|
|
83
|
+
const report = (uid, valid, fieldErrors) => {
|
|
84
|
+
hasValidated[uid] = true
|
|
85
|
+
|
|
86
|
+
if (!valid) {
|
|
87
|
+
errors[uid] = fieldErrors
|
|
88
|
+
} else {
|
|
89
|
+
delete errors[uid]
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
updateModelValue()
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Form is valid only if:
|
|
96
|
+
// 1. All registered fields have been validated (hasValidated[uid] = true)
|
|
97
|
+
// 2. No errors exist
|
|
98
|
+
const isValid = computed(() => {
|
|
99
|
+
// Check if all fields have been validated
|
|
100
|
+
const allFieldsValidated = Array.from(fields.keys()).every(uid => hasValidated[uid] === true)
|
|
101
|
+
|
|
102
|
+
// No errors and all fields validated
|
|
103
|
+
return allFieldsValidated && Object.keys(errors).length === 0
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
const updateModelValue = () => {
|
|
107
|
+
nextTick(() => {
|
|
108
|
+
if (props.modelValue !== null) {
|
|
109
|
+
emit('update:modelValue', isValid.value)
|
|
110
|
+
}
|
|
111
|
+
})
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const handleSubmit = async () => {
|
|
115
|
+
const result = await validate()
|
|
116
|
+
emit('submit', result)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
provide('formContext', {
|
|
120
|
+
register,
|
|
121
|
+
unregister,
|
|
122
|
+
validate: report,
|
|
123
|
+
validateOn: computed(() => props.validateOn)
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
defineExpose({
|
|
127
|
+
validate,
|
|
128
|
+
reset,
|
|
129
|
+
resetValidation,
|
|
130
|
+
isValid
|
|
131
|
+
})
|
|
132
|
+
</script>
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import Button from './Button.vue'
|
|
2
|
+
import Card from './Card.vue'
|
|
3
|
+
import HeroIcon from './HeroIcon.vue'
|
|
4
|
+
|
|
5
|
+
import AppBar from './AppBar.vue'
|
|
6
|
+
import Avatar from './Avatar.vue'
|
|
7
|
+
import Banner from './Banner.vue'
|
|
8
|
+
import Breadcrumb from './Breadcrumb.vue'
|
|
9
|
+
import Base from './base/Base.vue'
|
|
10
|
+
import Chip from './buttons/Chip.vue'
|
|
11
|
+
import Checkbox from './inputs/Checkbox.vue'
|
|
12
|
+
import ChipGroup from './buttons/ChipGroup.vue'
|
|
13
|
+
import ConfirmationModel from './ConfirmationModel.vue'
|
|
14
|
+
import DataTable from './DataTable.vue'
|
|
15
|
+
import DatePicker from './inputs/DatePicker.vue'
|
|
16
|
+
import Dropdown from './Dropdown.vue'
|
|
17
|
+
import DropdownItem from './DropdownItem.vue'
|
|
18
|
+
import DynamicForm from './forms/DynamicForm.vue'
|
|
19
|
+
import ExpansionPanel from './ExpansionPanel.vue'
|
|
20
|
+
import FileUpload from './inputs/FileUpload.vue'
|
|
21
|
+
import FilterSection from './filters/FilterSection.vue'
|
|
22
|
+
import Footer from './Footer.vue'
|
|
23
|
+
import Form from './forms/Form.vue'
|
|
24
|
+
import LayoutContainer from './LayoutContainer.vue'
|
|
25
|
+
import Main from './Main.vue'
|
|
26
|
+
import Modal from './Modal.vue'
|
|
27
|
+
import NavDrawer from './NavDrawer.vue'
|
|
28
|
+
import NavigationDrawer from './NavigationDrawer.vue'
|
|
29
|
+
import NavDrawerItem from './NavDrawerItem.vue'
|
|
30
|
+
import NumberField from './inputs/NumberField.vue'
|
|
31
|
+
import Pagination from './Pagination.vue'
|
|
32
|
+
import PApp from './PApp.vue'
|
|
33
|
+
import Search from './Search.vue'
|
|
34
|
+
import Searchbar from './filters/Searchbar.vue'
|
|
35
|
+
import Select from './inputs/Select.vue'
|
|
36
|
+
import StackedTableView from './StackedTableView.vue'
|
|
37
|
+
import Tab from './Tab.vue'
|
|
38
|
+
import Textarea from './inputs/Textarea.vue'
|
|
39
|
+
import TextField from './inputs/TextField.vue'
|
|
40
|
+
import Timeline from './Timeline.vue'
|
|
41
|
+
import TimelineItem from './TimelineItem.vue'
|
|
42
|
+
import Tooltip from './Tooltip.vue'
|
|
43
|
+
import ToastContainer from './Deprecated/ToastContainer.vue'
|
|
44
|
+
import ToastItem from './Deprecated/ToastItem.vue'
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
// export { Chip, ChipGroup, DataTable, Dropdown, FileUpload, Form }
|
|
48
|
+
export { Button, Card, HeroIcon,
|
|
49
|
+
AppBar,
|
|
50
|
+
Avatar,
|
|
51
|
+
Banner,
|
|
52
|
+
Base,
|
|
53
|
+
Breadcrumb,
|
|
54
|
+
Checkbox,
|
|
55
|
+
Chip,
|
|
56
|
+
ChipGroup,
|
|
57
|
+
ConfirmationModel,
|
|
58
|
+
DataTable,
|
|
59
|
+
DatePicker,
|
|
60
|
+
Dropdown,
|
|
61
|
+
DropdownItem,
|
|
62
|
+
DynamicForm,
|
|
63
|
+
ExpansionPanel,
|
|
64
|
+
FileUpload,
|
|
65
|
+
FilterSection,
|
|
66
|
+
Footer,
|
|
67
|
+
Form,
|
|
68
|
+
LayoutContainer,
|
|
69
|
+
Main,
|
|
70
|
+
Modal,
|
|
71
|
+
NavDrawer,
|
|
72
|
+
NavigationDrawer,
|
|
73
|
+
NavDrawerItem,
|
|
74
|
+
NumberField,
|
|
75
|
+
Pagination,
|
|
76
|
+
PApp,
|
|
77
|
+
Search,
|
|
78
|
+
Searchbar,
|
|
79
|
+
Select,
|
|
80
|
+
StackedTableView,
|
|
81
|
+
Tab,
|
|
82
|
+
Textarea,
|
|
83
|
+
TextField,
|
|
84
|
+
Timeline,
|
|
85
|
+
TimelineItem,
|
|
86
|
+
ToastContainer,
|
|
87
|
+
ToastItem,
|
|
88
|
+
Tooltip
|
|
89
|
+
}
|