pgo-uiux2 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/.env +1 -0
- package/.env.production +1 -0
- package/.prettierrc +13 -0
- package/.vscode/extensions.json +3 -0
- package/BUTTON_GUIDE.md +257 -0
- package/README.md +49 -0
- package/THEME_REFERENCE.md +310 -0
- package/eslint.config.ts +27 -0
- package/index.html +13 -0
- package/package.json +85 -0
- package/public/favicon.ico +0 -0
- package/src/App.vue +368 -0
- package/src/assets/fonts/Faruma.ttf +0 -0
- package/src/components/examples/AppBarExample.vue +101 -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 +170 -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 +347 -0
- package/src/components/pgo/Avatar.vue +139 -0
- package/src/components/pgo/Banner.vue +300 -0
- package/src/components/pgo/Breadcrumb.vue +101 -0
- package/src/components/pgo/Button.vue +171 -0
- package/src/components/pgo/Card.vue +178 -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/InputSearch.vue +194 -0
- package/src/components/pgo/LayoutContainer.vue +104 -0
- package/src/components/pgo/Main.vue +37 -0
- package/src/components/pgo/Modal.vue +273 -0
- package/src/components/pgo/NavDrawer.vue +127 -0
- package/src/components/pgo/NavDrawerItem.vue +161 -0
- package/src/components/pgo/NavigationDrawer.vue +849 -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 +15 -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/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/main.js +12 -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/index.js +96 -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 +85 -0
- package/src/pgo-components/pages/Home.vue +130 -0
- package/src/pgo-components/pages/ListView.vue +370 -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
- package/test.php +5 -0
- package/tsconfig.json +25 -0
- package/ui +31 -0
- package/ui.pgo.mv.conf +18 -0
- package/vite.config.js +42 -0
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="vdp-showcase space-y-8 p-6">
|
|
3
|
+
<h2 class="text-xl font-semibold text-center">
|
|
4
|
+
Vue DatePicker Showcase - based on @vuepic/vue-datepicker
|
|
5
|
+
</h2>
|
|
6
|
+
|
|
7
|
+
<DatePicker
|
|
8
|
+
v-model="date"
|
|
9
|
+
label="Birth Date"
|
|
10
|
+
|
|
11
|
+
/>
|
|
12
|
+
|
|
13
|
+
<!-- Basic date -->
|
|
14
|
+
<section>
|
|
15
|
+
<h3 class="mb-2 font-medium">
|
|
16
|
+
Basic Date
|
|
17
|
+
</h3>
|
|
18
|
+
<VueDatePicker
|
|
19
|
+
v-model="date"
|
|
20
|
+
format="yyyy-MM-dd"
|
|
21
|
+
placeholder="Select date"
|
|
22
|
+
:text-input="true"
|
|
23
|
+
|
|
24
|
+
/>
|
|
25
|
+
<p class="mt-2 text-sm">
|
|
26
|
+
Selected: {{ date ? date.toString() : 'None' }}
|
|
27
|
+
</p>
|
|
28
|
+
</section>
|
|
29
|
+
|
|
30
|
+
<!-- Range selection -->
|
|
31
|
+
<section>
|
|
32
|
+
<h3 class="mb-2 font-medium">
|
|
33
|
+
Range Selection
|
|
34
|
+
</h3>
|
|
35
|
+
<VueDatePicker
|
|
36
|
+
v-model="range"
|
|
37
|
+
range
|
|
38
|
+
auto-apply
|
|
39
|
+
:multi-calendars="2"
|
|
40
|
+
placeholder="Select date range"
|
|
41
|
+
/>
|
|
42
|
+
<p class="mt-2 text-sm">
|
|
43
|
+
Selected: {{ range && range[0] && range[1] ? range[0]?.toDateString() + ' — ' + range[1]?.toDateString() : 'None' }}
|
|
44
|
+
</p>
|
|
45
|
+
</section>
|
|
46
|
+
|
|
47
|
+
<!-- Date & time -->
|
|
48
|
+
<section>
|
|
49
|
+
<h3 class="mb-2 font-medium">
|
|
50
|
+
Date & Time
|
|
51
|
+
</h3>
|
|
52
|
+
<VueDatePicker
|
|
53
|
+
v-model="dateTime"
|
|
54
|
+
enable-time-picker
|
|
55
|
+
format="yyyy-MM-dd HH:mm"
|
|
56
|
+
placeholder="Select date & time"
|
|
57
|
+
text-input
|
|
58
|
+
/>
|
|
59
|
+
<p class="mt-2 text-sm">
|
|
60
|
+
Selected: {{ dateTime ? dateTime.toString() : 'None' }}
|
|
61
|
+
</p>
|
|
62
|
+
</section>
|
|
63
|
+
|
|
64
|
+
<!-- Inline calendar -->
|
|
65
|
+
<section>
|
|
66
|
+
<h3 class="mb-2 font-medium">
|
|
67
|
+
Inline Calendar
|
|
68
|
+
</h3>
|
|
69
|
+
<VueDatePicker
|
|
70
|
+
v-model="inlineDate"
|
|
71
|
+
inline
|
|
72
|
+
auto-apply
|
|
73
|
+
text-input
|
|
74
|
+
/>
|
|
75
|
+
<p class="mt-2 text-sm">
|
|
76
|
+
Selected: {{ inlineDate ? inlineDate.toDateString() : 'None' }}
|
|
77
|
+
</p>
|
|
78
|
+
</section>
|
|
79
|
+
|
|
80
|
+
<!-- Week / Month / Year pickers -->
|
|
81
|
+
<section>
|
|
82
|
+
<h3 class="mb-2 font-medium">
|
|
83
|
+
Week / Month / Year
|
|
84
|
+
</h3>
|
|
85
|
+
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
|
86
|
+
<div>
|
|
87
|
+
<label class="block mb-1 text-sm">Week Picker</label>
|
|
88
|
+
<VueDatePicker
|
|
89
|
+
v-model="week"
|
|
90
|
+
week-picker
|
|
91
|
+
auto-apply
|
|
92
|
+
text-input
|
|
93
|
+
/>
|
|
94
|
+
</div>
|
|
95
|
+
<div>
|
|
96
|
+
<label class="block mb-1 text-sm">Month Picker</label>
|
|
97
|
+
<VueDatePicker
|
|
98
|
+
v-model="month"
|
|
99
|
+
month-picker
|
|
100
|
+
auto-apply
|
|
101
|
+
text-input
|
|
102
|
+
/>
|
|
103
|
+
</div>
|
|
104
|
+
<div>
|
|
105
|
+
<label class="block mb-1 text-sm">Year Picker</label>
|
|
106
|
+
<VueDatePicker
|
|
107
|
+
v-model="year"
|
|
108
|
+
year-picker
|
|
109
|
+
auto-apply
|
|
110
|
+
text-input
|
|
111
|
+
/>
|
|
112
|
+
</div>
|
|
113
|
+
</div>
|
|
114
|
+
</section>
|
|
115
|
+
|
|
116
|
+
<!-- Min / Max and disabled dates -->
|
|
117
|
+
<section>
|
|
118
|
+
<h3 class="mb-2 font-medium">
|
|
119
|
+
Bounded + Disabled Weekends
|
|
120
|
+
</h3>
|
|
121
|
+
<VueDatePicker
|
|
122
|
+
v-model="bounded"
|
|
123
|
+
:min-date="minDate"
|
|
124
|
+
:max-date="maxDate"
|
|
125
|
+
:disabled-dates="disabledDates"
|
|
126
|
+
format="PPPP"
|
|
127
|
+
placeholder="Pick within bounds"
|
|
128
|
+
text-input
|
|
129
|
+
/>
|
|
130
|
+
<p class="mt-2 text-sm">
|
|
131
|
+
Selected: {{ bounded ? bounded.toDateString() : 'None' }}
|
|
132
|
+
</p>
|
|
133
|
+
<p class="text-xs text-gray-500">
|
|
134
|
+
Bounds: {{ minDate.toDateString() }} — {{ maxDate.toDateString() }}
|
|
135
|
+
</p>
|
|
136
|
+
</section>
|
|
137
|
+
|
|
138
|
+
<!-- Multiple dates -->
|
|
139
|
+
<section>
|
|
140
|
+
<h3 class="mb-2 font-medium">
|
|
141
|
+
Multiple Dates
|
|
142
|
+
</h3>
|
|
143
|
+
<VueDatePicker
|
|
144
|
+
v-model="multiDates"
|
|
145
|
+
multi-dates
|
|
146
|
+
:max-dates="5"
|
|
147
|
+
text-input
|
|
148
|
+
/>
|
|
149
|
+
<p class="mt-2 text-sm">
|
|
150
|
+
Selected: {{ multiDates && multiDates.length ? multiDates.map(d => d.toDateString()).join(', ') : 'None' }}
|
|
151
|
+
</p>
|
|
152
|
+
</section>
|
|
153
|
+
|
|
154
|
+
<!-- Position and Teleport -->
|
|
155
|
+
<!-- <section>
|
|
156
|
+
<h3 class="mb-2 font-medium">Position & Teleport</h3>
|
|
157
|
+
<VueDatePicker v-model="date" position="right" teleport="body" placeholder="Opens to the right" />
|
|
158
|
+
</section> -->
|
|
159
|
+
</div>
|
|
160
|
+
</template>
|
|
161
|
+
|
|
162
|
+
<script setup lang="ts">
|
|
163
|
+
import { ref } from 'vue'
|
|
164
|
+
import { VueDatePicker } from '@vuepic/vue-datepicker'
|
|
165
|
+
import '@vuepic/vue-datepicker/dist/main.css'
|
|
166
|
+
import DatePicker from '../pgo/inputs/DatePicker.vue'
|
|
167
|
+
|
|
168
|
+
// Basic date
|
|
169
|
+
const date = ref(null)
|
|
170
|
+
|
|
171
|
+
// Range selection
|
|
172
|
+
const range = ref(null)
|
|
173
|
+
|
|
174
|
+
// Date & time
|
|
175
|
+
const dateTime = ref(null)
|
|
176
|
+
|
|
177
|
+
// Inline calendar
|
|
178
|
+
const inlineDate = ref(null)
|
|
179
|
+
|
|
180
|
+
// Week / Month / Year pickers
|
|
181
|
+
const week = ref(null)
|
|
182
|
+
const month = ref(null)
|
|
183
|
+
const year = ref(null)
|
|
184
|
+
|
|
185
|
+
// Min / Max and disabled dates
|
|
186
|
+
const bounded = ref(null)
|
|
187
|
+
const minDate = ref(new Date(new Date().getFullYear(), new Date().getMonth(), 5))
|
|
188
|
+
const maxDate = ref(new Date(new Date().getFullYear(), new Date().getMonth(), 25))
|
|
189
|
+
|
|
190
|
+
function disabledDates(d) {
|
|
191
|
+
// Example: disable weekends
|
|
192
|
+
const day = d.getDay()
|
|
193
|
+
return day === 0 || day === 6
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Multiple selections
|
|
197
|
+
const multiDates = ref([])
|
|
198
|
+
</script>
|
|
199
|
+
|
|
200
|
+
<style scoped>
|
|
201
|
+
.vdp-showcase {
|
|
202
|
+
font-family: system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, sans-serif;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/* Theme token mapping for @vuepic/vue-datepicker via CSS variables */
|
|
206
|
+
/* DatePicker variables are now set globally in global.css */
|
|
207
|
+
|
|
208
|
+
/* Optional: ensure the datepicker input uses tokenized radius when library falls back */
|
|
209
|
+
:deep(.dp__input_wrap) {
|
|
210
|
+
border-radius: var(--vts-radius-md);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
</style>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="p-6 space-y-8">
|
|
3
|
+
<h2 class="text-xl font-semibold">DatePicker Examples</h2>
|
|
4
|
+
|
|
5
|
+
<div class="space-y-4">
|
|
6
|
+
<h3 class="font-medium">Single Date</h3>
|
|
7
|
+
<DatePicker v-model="single" placeholder="Pick a date" />
|
|
8
|
+
<div class="text-sm text-[var(--vts-color-textSecondary)]">Selected: {{ single && single.toDateString() }}</div>
|
|
9
|
+
</div>
|
|
10
|
+
|
|
11
|
+
<div class="space-y-4">
|
|
12
|
+
<h3 class="font-medium">Range</h3>
|
|
13
|
+
<DatePicker v-model="range" :range="true" placeholder="Pick a date range" />
|
|
14
|
+
<div class="text-sm text-[var(--vts-color-textSecondary)]">Selected: {{ range && range[0] && range[0].toDateString() }} — {{ range && range[1] && range[1].toDateString() }}</div>
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
<div class="space-y-4">
|
|
18
|
+
<h3 class="font-medium">With Constraints</h3>
|
|
19
|
+
<DatePicker v-model="bounded" :min="min" :max="max" />
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
</template>
|
|
23
|
+
|
|
24
|
+
<script setup lang="ts">
|
|
25
|
+
import { ref } from 'vue'
|
|
26
|
+
import DatePicker from '../pgo/inputs/DatePicker.vue'
|
|
27
|
+
|
|
28
|
+
const single = ref<Date | null>(null)
|
|
29
|
+
const range = ref<[Date | null, Date | null] | null>(null)
|
|
30
|
+
const bounded = ref<Date | null>(null)
|
|
31
|
+
const min = new Date()
|
|
32
|
+
const max = new Date(new Date().getFullYear(), 11, 31)
|
|
33
|
+
</script>
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<Card
|
|
3
|
+
:title="'Form Example'"
|
|
4
|
+
class="mb-4"
|
|
5
|
+
>
|
|
6
|
+
<Form
|
|
7
|
+
v-model="form"
|
|
8
|
+
:rules="rules"
|
|
9
|
+
:validate-on="'change'"
|
|
10
|
+
submit-text="Submit"
|
|
11
|
+
>
|
|
12
|
+
<template #default="{ values, errors, setField }">
|
|
13
|
+
<div class="space-y-3">
|
|
14
|
+
<InputSearch
|
|
15
|
+
v-model="values.name"
|
|
16
|
+
label="Name"
|
|
17
|
+
:error-messages="errors.name || []"
|
|
18
|
+
:placeholder="'Your name'"
|
|
19
|
+
@input="val => setField('name', val)"
|
|
20
|
+
/>
|
|
21
|
+
|
|
22
|
+
<Select
|
|
23
|
+
v-model="values.category"
|
|
24
|
+
label="Category"
|
|
25
|
+
:items="categories"
|
|
26
|
+
size="md"
|
|
27
|
+
item-title="text"
|
|
28
|
+
item-value="value"
|
|
29
|
+
:error-messages="errors.category || []"
|
|
30
|
+
@change="val => setField('category', val)"
|
|
31
|
+
/>
|
|
32
|
+
|
|
33
|
+
<FileUpload
|
|
34
|
+
v-model="values.files"
|
|
35
|
+
label="Attachments"
|
|
36
|
+
:error-messages="errors.files || []"
|
|
37
|
+
accept="image/*,.pdf"
|
|
38
|
+
rounded=""
|
|
39
|
+
:max-size-mb="2"
|
|
40
|
+
@change="files => setField('files', files)"
|
|
41
|
+
/>
|
|
42
|
+
</div>
|
|
43
|
+
</template>
|
|
44
|
+
</Form>
|
|
45
|
+
</Card>
|
|
46
|
+
</template>
|
|
47
|
+
|
|
48
|
+
<script setup>
|
|
49
|
+
import { ref } from 'vue'
|
|
50
|
+
import Card from '../pgo/Card.vue'
|
|
51
|
+
import Form from '../pgo/forms/Form.vue'
|
|
52
|
+
import InputSearch from '../pgo/InputSearch.vue'
|
|
53
|
+
import Select from '../pgo/inputs/Select.vue'
|
|
54
|
+
import FileUpload from '../pgo/inputs/FileUpload.vue'
|
|
55
|
+
const form = ref({ name: '', category: '', files: [] })
|
|
56
|
+
|
|
57
|
+
const rules = {
|
|
58
|
+
name: [
|
|
59
|
+
(v) => (v && String(v).trim().length >= 2) || 'Name is required (min 2 chars)'
|
|
60
|
+
],
|
|
61
|
+
category: [
|
|
62
|
+
(v) => !!v || 'Category is required'
|
|
63
|
+
],
|
|
64
|
+
files: [
|
|
65
|
+
(arr) => (Array.isArray(arr) && arr.length > 0) || 'At least one file'
|
|
66
|
+
]
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const categories = [
|
|
70
|
+
{ text: 'General', value: 'general' },
|
|
71
|
+
{ text: 'Bug', value: 'bug' },
|
|
72
|
+
{ text: 'Feature', value: 'feature' },
|
|
73
|
+
]
|
|
74
|
+
</script>
|
|
75
|
+
|
|
76
|
+
<style scoped>
|
|
77
|
+
</style>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import AppBar from "./pgo/AppBar.vue";
|
|
2
|
+
import Button from "./pgo/Button.vue";
|
|
3
|
+
import Card from "./pgo/Card.vue";
|
|
4
|
+
import HeroIcon from "./pgo/HeroIcon.vue";
|
|
5
|
+
import Select from "./pgo/inputs/Select.vue";
|
|
6
|
+
import FileUpload from "./pgo/inputs/FileUpload.vue";
|
|
7
|
+
import Form from "./pgo/forms/Form.vue";
|
|
8
|
+
// import NavDrawer from './pgo/NavDrawer.vue'
|
|
9
|
+
import NavigationDrawer from "./pgo/NavigationDrawer.vue";
|
|
10
|
+
import NavDrawer from "./pgo/NavDrawer.vue";
|
|
11
|
+
import NavDrawerItem from "./pgo/NavDrawerItem.vue";
|
|
12
|
+
import OldAppBar from "./pgo/OldAppBar.vue";
|
|
13
|
+
|
|
14
|
+
export {
|
|
15
|
+
AppBar,
|
|
16
|
+
OldAppBar,
|
|
17
|
+
Button,
|
|
18
|
+
Card,
|
|
19
|
+
HeroIcon,
|
|
20
|
+
Select,
|
|
21
|
+
NavDrawer,
|
|
22
|
+
NavDrawerItem,
|
|
23
|
+
FileUpload,
|
|
24
|
+
Form,
|
|
25
|
+
};
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<header
|
|
3
|
+
ref="el"
|
|
4
|
+
data-component="appbar"
|
|
5
|
+
:dir="rtl ? 'rtl' : 'ltr'"
|
|
6
|
+
:class="[
|
|
7
|
+
'w-full flex flex-col transition-transform duration-300',
|
|
8
|
+
appBarZClass,
|
|
9
|
+
fixed ? 'fixed top-0 left-0' : floating ? 'absolute top-0 left-1/2 -translate-x-1/2 rounded-xl shadow-lg' : 'relative',
|
|
10
|
+
elevationClass,
|
|
11
|
+
hidden ? 'hidden' : '',
|
|
12
|
+
color,
|
|
13
|
+
border,
|
|
14
|
+
p,
|
|
15
|
+
m,
|
|
16
|
+
customClass,
|
|
17
|
+
densityClass,
|
|
18
|
+
collapseClass
|
|
19
|
+
]"
|
|
20
|
+
:style="[themeStyles, customStyle, appBarZStyle]"
|
|
21
|
+
>
|
|
22
|
+
<div :class="['max-w-7xl mx-auto w-full flex items-center justify-between', innerDirectionClass, dense ? 'h-12' : 'h-16']">
|
|
23
|
+
<!-- DYNAMIC REGION ORDER -->
|
|
24
|
+
<template v-for="region in orderedRegions" :key="region">
|
|
25
|
+
<!-- PREPEND -->
|
|
26
|
+
<template v-if="region === 'prepend'">
|
|
27
|
+
<div :class="['flex items-center', prependClass]">
|
|
28
|
+
<slot name="prepend" />
|
|
29
|
+
|
|
30
|
+
<!-- drawer/menu button -->
|
|
31
|
+
<button
|
|
32
|
+
v-if="drawer || menu"
|
|
33
|
+
@click="$emit('toggle-drawer')"
|
|
34
|
+
class="md:hidden inline-flex items-center justify-center .vts-pa-2 rounded-md"
|
|
35
|
+
>
|
|
36
|
+
<slot name="drawer-icon">
|
|
37
|
+
<HeroIcon name="bars-3" size="24" color="text-gray-600" />
|
|
38
|
+
</slot>
|
|
39
|
+
</button>
|
|
40
|
+
|
|
41
|
+
<!-- TITLE -->
|
|
42
|
+
<div :class="['shrink-0 flex items-center', titleMarginClass, titleAlignClass]">
|
|
43
|
+
<slot name="title">
|
|
44
|
+
<span :class="['font-semibold', titleClass]">{{ title }}</span>
|
|
45
|
+
</slot>
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
</template>
|
|
49
|
+
|
|
50
|
+
<!-- NAV -->
|
|
51
|
+
<template v-if="region === 'nav'">
|
|
52
|
+
<nav :class="navClass">
|
|
53
|
+
<slot name="nav" />
|
|
54
|
+
</nav>
|
|
55
|
+
</template>
|
|
56
|
+
|
|
57
|
+
<!-- ACTIONS -->
|
|
58
|
+
<template v-if="region === 'actions'">
|
|
59
|
+
<div :class="['flex items-center', actionsSpaceClass]">
|
|
60
|
+
<slot name="actions" />
|
|
61
|
+
|
|
62
|
+
<!-- Overflow actions -->
|
|
63
|
+
<div v-if="overflowActions.length" class="relative">
|
|
64
|
+
<button @click="toggleOverflow" class="p-2 rounded-md hover:bg-black/5 dark:hover:bg-white/10">
|
|
65
|
+
<svg class="h-5 w-5" fill="none" stroke="currentColor">
|
|
66
|
+
<circle cx="12" cy="12" r="2" />
|
|
67
|
+
<circle cx="19" cy="12" r="2" />
|
|
68
|
+
<circle cx="5" cy="12" r="2" />
|
|
69
|
+
</svg>
|
|
70
|
+
</button>
|
|
71
|
+
|
|
72
|
+
<div v-if="isOverflowOpen" :class="['absolute vts-mt-2 w-40 rounded shadow-lg z-50', overflowPositionClass]">
|
|
73
|
+
<ul>
|
|
74
|
+
<li v-for="(action, i) in overflowActions" :key="i">
|
|
75
|
+
<button @click="action.onClick" class="w-full text-left vts-px-4 vts-py-2 hover:bg-black/5 dark:hover:bg-white/10">
|
|
76
|
+
<span v-if="action.icon" class="vts-mr-2">
|
|
77
|
+
<component :is="action.icon" />
|
|
78
|
+
</span>
|
|
79
|
+
{{ action.label }}
|
|
80
|
+
</button>
|
|
81
|
+
</li>
|
|
82
|
+
</ul>
|
|
83
|
+
</div>
|
|
84
|
+
</div>
|
|
85
|
+
|
|
86
|
+
<!-- Language Selector -->
|
|
87
|
+
<div class="relative" ref="langMenuRef">
|
|
88
|
+
<button @click="toggleLangMenu" class="px-2 py-1 rounded-md hover:bg-black/5 dark:hover:bg-white/10">
|
|
89
|
+
{{ currentLangCode }}
|
|
90
|
+
</button>
|
|
91
|
+
<div
|
|
92
|
+
v-if="isLangOpen"
|
|
93
|
+
:class="['absolute vts-mt-2 w-28 rounded shadow-lg z-50', overflowPositionClass]"
|
|
94
|
+
:style="{ backgroundColor: 'var(--vts-color-surfaceElevated)', borderColor: 'var(--vts-color-divider)' }"
|
|
95
|
+
>
|
|
96
|
+
<ul>
|
|
97
|
+
<li v-for="(lang, i) in languagesList" :key="i">
|
|
98
|
+
<button
|
|
99
|
+
@click="selectLanguage(lang)"
|
|
100
|
+
class="w-full text-left vts-px-3 vts-py-2 hover:bg-black/5 dark:hover:bg-white/10"
|
|
101
|
+
>
|
|
102
|
+
{{ getLangLabel(lang) }}
|
|
103
|
+
</button>
|
|
104
|
+
</li>
|
|
105
|
+
</ul>
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
|
|
109
|
+
<slot name="append" />
|
|
110
|
+
</div>
|
|
111
|
+
</template>
|
|
112
|
+
</template>
|
|
113
|
+
</div>
|
|
114
|
+
|
|
115
|
+
<!-- MOBILE NAV -->
|
|
116
|
+
<div
|
|
117
|
+
v-show="isOpen"
|
|
118
|
+
class="md:hidden vts-border-sm"
|
|
119
|
+
:style="{ backgroundColor: 'var(--vts-color-surfaceElevated)', borderColor: 'var(--vts-color-divider)' }"
|
|
120
|
+
>
|
|
121
|
+
<div class="vts-px-2 vts-pt-2 vts-pb-3 vts-space-y-1">
|
|
122
|
+
<slot name="mobile-nav" />
|
|
123
|
+
</div>
|
|
124
|
+
</div>
|
|
125
|
+
</header>
|
|
126
|
+
</template>
|
|
127
|
+
|
|
128
|
+
<script setup lang="ts">
|
|
129
|
+
import { ref, computed, onMounted, onBeforeUnmount, inject, watch } from 'vue'
|
|
130
|
+
import type { PropType } from 'vue'
|
|
131
|
+
import { HeroIcon } from './'
|
|
132
|
+
import { globalRtl } from '../../pgo-components/lib/core/rtl/rtl'
|
|
133
|
+
|
|
134
|
+
const themeStyles = computed(() => ({
|
|
135
|
+
backgroundColor: 'var(--vts-color-surfaceElevated)',
|
|
136
|
+
color: 'var(--vts-color-text)',
|
|
137
|
+
boxShadow: props.elevation ? `var(--vts-elevation-${props.elevation})` : props.shadow ? 'var(--vts-shadow-md)' : undefined,
|
|
138
|
+
borderBottom: '1px solid var(--vts-color-border)'
|
|
139
|
+
}))
|
|
140
|
+
|
|
141
|
+
/* ---------------------------------------------
|
|
142
|
+
* PROPS — merged (old props kept)
|
|
143
|
+
----------------------------------------------*/
|
|
144
|
+
const props = defineProps({
|
|
145
|
+
id: {
|
|
146
|
+
type: String,
|
|
147
|
+
default: () => `appbar-${Math.random().toString(36).slice(2, 9)}`
|
|
148
|
+
},
|
|
149
|
+
|
|
150
|
+
title: String,
|
|
151
|
+
titleAlign: { type: String, default: 'left' },
|
|
152
|
+
titleClass: String,
|
|
153
|
+
|
|
154
|
+
fixed: Boolean,
|
|
155
|
+
floating: Boolean,
|
|
156
|
+
|
|
157
|
+
elevation: [String, Number],
|
|
158
|
+
shadow: String, // kept for compatibility
|
|
159
|
+
|
|
160
|
+
color: String,
|
|
161
|
+
border: String,
|
|
162
|
+
p: String,
|
|
163
|
+
m: String,
|
|
164
|
+
hidden: Boolean,
|
|
165
|
+
customClass: String,
|
|
166
|
+
customStyle: [String, Object],
|
|
167
|
+
|
|
168
|
+
zIndex: { type: [String, Number], default: undefined },
|
|
169
|
+
|
|
170
|
+
drawer: Boolean,
|
|
171
|
+
menu: Boolean,
|
|
172
|
+
overflowActions: { type: Array as PropType<Array<{ label: string; icon?: any; onClick?: () => void }>>, default: () => [] },
|
|
173
|
+
|
|
174
|
+
// Language selector
|
|
175
|
+
languages: { type: Array as PropType<string[]>, default: () => ['en', 'dv'] },
|
|
176
|
+
|
|
177
|
+
rtl: { type: Boolean, default: globalRtl.value },
|
|
178
|
+
|
|
179
|
+
dense: Boolean,
|
|
180
|
+
collapseOnScroll: Boolean
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
/* ---------------------------------------------
|
|
184
|
+
* layout integration
|
|
185
|
+
----------------------------------------------*/
|
|
186
|
+
const layout = inject('layout', null)
|
|
187
|
+
const el = ref<HTMLElement | null>(null)
|
|
188
|
+
const height = ref(0)
|
|
189
|
+
|
|
190
|
+
function updateHeight() {
|
|
191
|
+
if (!el.value) return
|
|
192
|
+
height.value = el.value.getBoundingClientRect().height
|
|
193
|
+
if (layout)
|
|
194
|
+
layout.register('appBar', props.id, {
|
|
195
|
+
height: height.value,
|
|
196
|
+
dense: props.dense
|
|
197
|
+
})
|
|
198
|
+
// console.log('AppBar registered with height:', height.value, ' and dense:', props.dense)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
onMounted(() => {
|
|
202
|
+
updateHeight()
|
|
203
|
+
const ro = new ResizeObserver(updateHeight)
|
|
204
|
+
if (el.value) ro.observe(el.value)
|
|
205
|
+
|
|
206
|
+
if (props.collapseOnScroll) window.addEventListener('scroll', onScroll, { passive: true })
|
|
207
|
+
document.addEventListener('click', onDocumentClick, { capture: true })
|
|
208
|
+
|
|
209
|
+
onBeforeUnmount(() => {
|
|
210
|
+
ro.disconnect()
|
|
211
|
+
if (props.collapseOnScroll) window.removeEventListener('scroll', onScroll)
|
|
212
|
+
document.removeEventListener('click', onDocumentClick, { capture: true })
|
|
213
|
+
if (layout) layout.unregister('appBar', props.id)
|
|
214
|
+
})
|
|
215
|
+
})
|
|
216
|
+
|
|
217
|
+
/* ---------------------------------------------
|
|
218
|
+
* collapse on scroll
|
|
219
|
+
----------------------------------------------*/
|
|
220
|
+
let lastScroll = 0
|
|
221
|
+
const collapseClass = ref('')
|
|
222
|
+
|
|
223
|
+
function onScroll() {
|
|
224
|
+
const y = window.scrollY
|
|
225
|
+
const delta = y - lastScroll
|
|
226
|
+
lastScroll = y
|
|
227
|
+
|
|
228
|
+
if (y > 0 && delta > 0) collapseClass.value = '-translate-y-12 opacity-95'
|
|
229
|
+
else collapseClass.value = ''
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/* ---------------------------------------------
|
|
233
|
+
* OLD RTL logic (preserved)
|
|
234
|
+
----------------------------------------------*/
|
|
235
|
+
const orderedRegions = computed(() => (props.rtl ? ['actions', 'nav', 'prepend'] : ['prepend', 'nav', 'actions']))
|
|
236
|
+
|
|
237
|
+
const innerDirectionClass = computed(() => (props.rtl ? 'flex-row-reverse' : 'flex-row'))
|
|
238
|
+
|
|
239
|
+
const prependClass = computed(() => (props.rtl ? 'mr-2' : ''))
|
|
240
|
+
const titleMarginClass = computed(() => (props.rtl ? 'mr-2' : 'ml-2'))
|
|
241
|
+
|
|
242
|
+
const titleAlignClass = computed(() => {
|
|
243
|
+
const a = props.titleAlign
|
|
244
|
+
const RTL = props.rtl
|
|
245
|
+
|
|
246
|
+
if (RTL)
|
|
247
|
+
return {
|
|
248
|
+
center: 'justify-center text-center w-full',
|
|
249
|
+
left: 'justify-end text-right w-full',
|
|
250
|
+
right: ''
|
|
251
|
+
}[a]
|
|
252
|
+
|
|
253
|
+
return {
|
|
254
|
+
center: 'justify-center text-center w-full',
|
|
255
|
+
right: 'justify-end text-right w-full',
|
|
256
|
+
left: ''
|
|
257
|
+
}[a]
|
|
258
|
+
})
|
|
259
|
+
|
|
260
|
+
const navClass = computed(() =>
|
|
261
|
+
props.rtl ? 'hidden md:flex md:mr-6 md:space-x-4 items-center' : 'hidden md:flex md:ml-6 md:space-x-4 items-center'
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
const actionsSpaceClass = computed(() => (props.rtl ? 'space-x-2 space-x-reverse' : 'space-x-2'))
|
|
265
|
+
|
|
266
|
+
const overflowPositionClass = computed(() => (props.rtl ? 'left-0' : 'right-0'))
|
|
267
|
+
|
|
268
|
+
/* ---------------------------------------------
|
|
269
|
+
* Overflow menu
|
|
270
|
+
----------------------------------------------*/
|
|
271
|
+
const isOverflowOpen = ref(false)
|
|
272
|
+
const toggleOverflow = () => (isOverflowOpen.value = !isOverflowOpen.value)
|
|
273
|
+
|
|
274
|
+
/* ---------------------------------------------
|
|
275
|
+
* Language selector (i18n)
|
|
276
|
+
----------------------------------------------*/
|
|
277
|
+
const { setLanguage, language } = inject('i18n') as any
|
|
278
|
+
const isLangOpen = ref(false)
|
|
279
|
+
const toggleLangMenu = () => (isLangOpen.value = !isLangOpen.value)
|
|
280
|
+
const langMenuRef = ref<HTMLElement | null>(null)
|
|
281
|
+
const languagesList = computed<string[]>(() =>
|
|
282
|
+
Array.isArray(props.languages) && props.languages.length ? props.languages : ['en', 'dv']
|
|
283
|
+
)
|
|
284
|
+
const currentLangCode = computed(() => {
|
|
285
|
+
const val = (language && (language.value ?? language)) as string
|
|
286
|
+
const code = (val || '').slice(0, 2)
|
|
287
|
+
if (code === 'dv') return 'ދިވެހި'
|
|
288
|
+
return (code || 'en').toUpperCase()
|
|
289
|
+
})
|
|
290
|
+
function selectLanguage(lang: string) {
|
|
291
|
+
if (typeof setLanguage === 'function') setLanguage(lang)
|
|
292
|
+
window.location.reload()
|
|
293
|
+
isLangOpen.value = false
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function onDocumentClick(e: MouseEvent) {
|
|
297
|
+
if (!isLangOpen.value) return
|
|
298
|
+
isLangOpen.value = false
|
|
299
|
+
// const root = langMenuRef.value
|
|
300
|
+
// const target = e.target as Node | null
|
|
301
|
+
// if (!root || !target) {
|
|
302
|
+
// isLangOpen.value = false
|
|
303
|
+
// return
|
|
304
|
+
// }
|
|
305
|
+
// if (!root.contains(target)) {
|
|
306
|
+
// isLangOpen.value = false
|
|
307
|
+
// }
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function getLangLabel(lang: string) {
|
|
311
|
+
const val = (language && (language.value ?? language)) as string
|
|
312
|
+
const isDv = (val || '').slice(0, 2) === 'dv'
|
|
313
|
+
if (isDv) {
|
|
314
|
+
if (lang === 'en') return 'އިނގިރޭސި'
|
|
315
|
+
if (lang === 'dv') return 'ދިވެހި'
|
|
316
|
+
}
|
|
317
|
+
return (lang || '').toUpperCase()
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/* ---------------------------------------------
|
|
321
|
+
* elevation + zIndex logic (merged)
|
|
322
|
+
----------------------------------------------*/
|
|
323
|
+
const elevationClass = computed(() => {
|
|
324
|
+
if (props.shadow) return props.shadow
|
|
325
|
+
if (props.elevation) return `shadow-${props.elevation}`
|
|
326
|
+
return ''
|
|
327
|
+
})
|
|
328
|
+
|
|
329
|
+
const appBarZClass = computed(() => {
|
|
330
|
+
if (typeof props.zIndex === 'string' && props.zIndex.startsWith('z-')) return props.zIndex
|
|
331
|
+
return 'z-30'
|
|
332
|
+
})
|
|
333
|
+
const appBarZStyle = computed(() => (typeof props.zIndex === 'number' ? { zIndex: props.zIndex } : undefined))
|
|
334
|
+
|
|
335
|
+
/* ---------------------------------------------
|
|
336
|
+
* density class
|
|
337
|
+
----------------------------------------------*/
|
|
338
|
+
const densityClass = computed(() => (props.dense ? 'py-1' : ''))
|
|
339
|
+
|
|
340
|
+
/* ---------------------------------------------
|
|
341
|
+
* mobile nav
|
|
342
|
+
----------------------------------------------*/
|
|
343
|
+
const isOpen = ref(false)
|
|
344
|
+
|
|
345
|
+
/* expose for template */
|
|
346
|
+
</script>
|
|
347
|
+
|