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,223 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<header
|
|
3
|
+
:class="[
|
|
4
|
+
'w-full flex flex-col',
|
|
5
|
+
appBarZClass,
|
|
6
|
+
appBarZClass,
|
|
7
|
+
fixed
|
|
8
|
+
? 'fixed top-0 left-0'
|
|
9
|
+
: floating
|
|
10
|
+
? 'absolute top-0 left-1/2 -translate-x-1/2 rounded-xl shadow-lg'
|
|
11
|
+
: '',
|
|
12
|
+
elevation ? `shadow-${elevation}` : shadow ? shadow : '',
|
|
13
|
+
hidden ? 'hidden' : '',
|
|
14
|
+
color,
|
|
15
|
+
border,
|
|
16
|
+
p,
|
|
17
|
+
m,
|
|
18
|
+
customClass,
|
|
19
|
+
]"
|
|
20
|
+
:dir="rtl ? 'rtl' : 'ltr'"
|
|
21
|
+
:style="[customStyle, appBarZStyle]"
|
|
22
|
+
data-component="appbar"
|
|
23
|
+
>
|
|
24
|
+
<div
|
|
25
|
+
:class="[
|
|
26
|
+
'max-w-7xl mx-auto w-full flex items-center h-16 justify-between',
|
|
27
|
+
innerDirectionClass,
|
|
28
|
+
]"
|
|
29
|
+
>
|
|
30
|
+
<!-- ORDERED REGIONS -->
|
|
31
|
+
<template v-for="region in orderedRegions" :key="region">
|
|
32
|
+
<!-- PREPEND REGION -->
|
|
33
|
+
<template v-if="region === 'prepend'">
|
|
34
|
+
<div :class="['flex items-center', prependClass]">
|
|
35
|
+
<slot name="prepend" />
|
|
36
|
+
|
|
37
|
+
<button
|
|
38
|
+
v-if="drawer || menu"
|
|
39
|
+
@click="$emit('toggle-drawer')"
|
|
40
|
+
class="md:hidden inline-flex items-center justify-center p-2 rounded-md text-gray-600 hover:bg-gray-100"
|
|
41
|
+
>
|
|
42
|
+
<slot name="drawer-icon">
|
|
43
|
+
<HeroIcon name="bars-3" size="24" color="text-gray-600" />
|
|
44
|
+
</slot>
|
|
45
|
+
</button>
|
|
46
|
+
|
|
47
|
+
<div
|
|
48
|
+
:class="[
|
|
49
|
+
'flex-shrink-0 flex items-center',
|
|
50
|
+
titleMarginClass,
|
|
51
|
+
titleAlignClass,
|
|
52
|
+
]"
|
|
53
|
+
>
|
|
54
|
+
<slot name="title">
|
|
55
|
+
<span :class="['font-semibold', titleClass]">{{ title }}</span>
|
|
56
|
+
</slot>
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
</template>
|
|
60
|
+
|
|
61
|
+
<!-- NAV REGION -->
|
|
62
|
+
<template v-if="region === 'nav'">
|
|
63
|
+
<nav :class="navClass">
|
|
64
|
+
<slot name="nav" />
|
|
65
|
+
</nav>
|
|
66
|
+
</template>
|
|
67
|
+
|
|
68
|
+
<!-- ACTIONS REGION -->
|
|
69
|
+
<template v-if="region === 'actions'">
|
|
70
|
+
<div :class="['flex items-center', actionsSpaceClass]">
|
|
71
|
+
<slot name="actions" />
|
|
72
|
+
|
|
73
|
+
<!-- Overflow actions -->
|
|
74
|
+
<div v-if="overflowActions.length" class="relative">
|
|
75
|
+
<button
|
|
76
|
+
@click="toggleOverflow"
|
|
77
|
+
class="p-2 rounded-full hover:bg-gray-100 focus:outline-none"
|
|
78
|
+
>
|
|
79
|
+
<svg
|
|
80
|
+
class="h-5 w-5"
|
|
81
|
+
viewBox="0 0 24 24"
|
|
82
|
+
fill="none"
|
|
83
|
+
stroke="currentColor"
|
|
84
|
+
>
|
|
85
|
+
<circle cx="12" cy="12" r="2" />
|
|
86
|
+
<circle cx="19" cy="12" r="2" />
|
|
87
|
+
<circle cx="5" cy="12" r="2" />
|
|
88
|
+
</svg>
|
|
89
|
+
</button>
|
|
90
|
+
|
|
91
|
+
<div
|
|
92
|
+
v-if="isOverflowOpen"
|
|
93
|
+
:class="[
|
|
94
|
+
'absolute mt-2 w-40 bg-white rounded shadow-lg z-50',
|
|
95
|
+
overflowPositionClass,
|
|
96
|
+
]"
|
|
97
|
+
>
|
|
98
|
+
<ul>
|
|
99
|
+
<li v-for="(action, i) in overflowActions" :key="i">
|
|
100
|
+
<button
|
|
101
|
+
@click="action.onClick"
|
|
102
|
+
class="w-full text-left px-4 py-2 hover:bg-gray-100"
|
|
103
|
+
>
|
|
104
|
+
<span v-if="action.icon" class="mr-2">
|
|
105
|
+
<component :is="action.icon" />
|
|
106
|
+
</span>
|
|
107
|
+
{{ action.label }}
|
|
108
|
+
</button>
|
|
109
|
+
</li>
|
|
110
|
+
</ul>
|
|
111
|
+
</div>
|
|
112
|
+
</div>
|
|
113
|
+
|
|
114
|
+
<slot name="append" />
|
|
115
|
+
</div>
|
|
116
|
+
</template>
|
|
117
|
+
</template>
|
|
118
|
+
</div>
|
|
119
|
+
|
|
120
|
+
<!-- MOBILE NAV -->
|
|
121
|
+
<div v-show="isOpen" class="md:hidden bg-white border-t">
|
|
122
|
+
<div class="px-2 pt-2 pb-3 space-y-1">
|
|
123
|
+
<slot name="mobile-nav" />
|
|
124
|
+
</div>
|
|
125
|
+
</div>
|
|
126
|
+
</header>
|
|
127
|
+
</template>
|
|
128
|
+
|
|
129
|
+
<script setup>
|
|
130
|
+
import { ref, computed, onMounted } from "vue";
|
|
131
|
+
import { HeroIcon } from "../pgo/";
|
|
132
|
+
|
|
133
|
+
const props = defineProps({
|
|
134
|
+
title: String,
|
|
135
|
+
titleAlign: { type: String, default: "left" },
|
|
136
|
+
titleClass: String,
|
|
137
|
+
fixed: Boolean,
|
|
138
|
+
floating: Boolean,
|
|
139
|
+
elevation: [String, Number],
|
|
140
|
+
shadow: String,
|
|
141
|
+
color: String,
|
|
142
|
+
border: String,
|
|
143
|
+
p: String,
|
|
144
|
+
m: String,
|
|
145
|
+
hidden: Boolean,
|
|
146
|
+
customClass: String,
|
|
147
|
+
customStyle: [String, Object],
|
|
148
|
+
zIndex: { type: [String, Number], default: undefined },
|
|
149
|
+
drawer: Boolean,
|
|
150
|
+
menu: Boolean,
|
|
151
|
+
overflowActions: { type: Array, default: () => [] },
|
|
152
|
+
rtl: Boolean,
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
const emit = defineEmits(["toggle-drawer"]);
|
|
156
|
+
|
|
157
|
+
const isOpen = ref(false);
|
|
158
|
+
const isOverflowOpen = ref(false);
|
|
159
|
+
|
|
160
|
+
const toggleOverflow = () => (isOverflowOpen.value = !isOverflowOpen.value);
|
|
161
|
+
|
|
162
|
+
const orderedRegions = computed(() =>
|
|
163
|
+
props.rtl ? ["actions", "nav", "prepend"] : ["prepend", "nav", "actions"]
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
const innerDirectionClass = computed(() =>
|
|
167
|
+
props.rtl ? "flex-row-reverse" : "flex-row"
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
const prependClass = computed(() => (props.rtl ? "mr-2" : ""));
|
|
171
|
+
|
|
172
|
+
const titleMarginClass = computed(() => (props.rtl ? "mr-2" : "ml-2"));
|
|
173
|
+
|
|
174
|
+
const titleAlignClass = computed(() => {
|
|
175
|
+
const align = props.titleAlign;
|
|
176
|
+
if (props.rtl) {
|
|
177
|
+
return {
|
|
178
|
+
center: "justify-center text-center w-full",
|
|
179
|
+
left: "justify-end text-right w-full",
|
|
180
|
+
right: "",
|
|
181
|
+
}[align];
|
|
182
|
+
}
|
|
183
|
+
return {
|
|
184
|
+
center: "justify-center text-center w-full",
|
|
185
|
+
right: "justify-end text-right w-full",
|
|
186
|
+
left: "",
|
|
187
|
+
}[align];
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
const navClass = computed(() =>
|
|
191
|
+
props.rtl
|
|
192
|
+
? "hidden md:flex md:mr-6 md:space-x-4 items-center"
|
|
193
|
+
: "hidden md:flex md:ml-6 md:space-x-4 items-center"
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
const actionsSpaceClass = computed(() =>
|
|
197
|
+
props.rtl ? "space-x-2 space-x-reverse" : "space-x-2"
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
const overflowPositionClass = computed(() =>
|
|
201
|
+
props.rtl ? "left-0" : "right-0"
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
// z-index support for appbar
|
|
205
|
+
const appBarZClass = computed(() => {
|
|
206
|
+
if (props.zIndex !== undefined && typeof props.zIndex === "string") {
|
|
207
|
+
if (String(props.zIndex).startsWith("z-")) return String(props.zIndex);
|
|
208
|
+
}
|
|
209
|
+
return "z-30";
|
|
210
|
+
});
|
|
211
|
+
const appBarZStyle = computed(() => {
|
|
212
|
+
if (props.zIndex !== undefined && typeof props.zIndex === "number") {
|
|
213
|
+
return { zIndex: props.zIndex };
|
|
214
|
+
}
|
|
215
|
+
return undefined;
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
onMounted(() => {
|
|
219
|
+
const savedRtl = localStorage.getItem("rtlSetting");
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
});
|
|
223
|
+
</script>
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div :class="['min-h-screen flex flex-col w-full', rtlClass]" ref="root">
|
|
3
|
+
<slot />
|
|
4
|
+
</div>
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<script setup lang="ts">
|
|
8
|
+
import {
|
|
9
|
+
provide,
|
|
10
|
+
ref,
|
|
11
|
+
reactive,
|
|
12
|
+
watch,
|
|
13
|
+
onMounted,
|
|
14
|
+
onBeforeUnmount,
|
|
15
|
+
computed,
|
|
16
|
+
toRaw,
|
|
17
|
+
} from "vue";
|
|
18
|
+
import { globalRtl } from "../../pgo-components/lib/core/rtl/rtl";
|
|
19
|
+
|
|
20
|
+
// Layout registry (v-app coordinator)
|
|
21
|
+
const app = reactive({
|
|
22
|
+
appBars: new Map(),
|
|
23
|
+
footers: new Map(),
|
|
24
|
+
drawers: new Map(),
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
function register(type: "appBar" | "footer" | "drawer", id: string, meta: any) {
|
|
28
|
+
if (type === "appBar") app.appBars.set(id, meta);
|
|
29
|
+
if (type === "footer") app.footers.set(id, meta);
|
|
30
|
+
if (type === "drawer") app.drawers.set(id, meta);
|
|
31
|
+
}
|
|
32
|
+
function unregister(type: "appBar" | "footer" | "drawer", id: string) {
|
|
33
|
+
if (type === "appBar") app.appBars.delete(id);
|
|
34
|
+
if (type === "footer") app.footers.delete(id);
|
|
35
|
+
if (type === "drawer") app.drawers.delete(id);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const root = ref<HTMLElement | null>(null);
|
|
39
|
+
|
|
40
|
+
// Derived metrics
|
|
41
|
+
const topOffset = computed(() => {
|
|
42
|
+
let h = 0;
|
|
43
|
+
for (const [, meta] of app.appBars) {
|
|
44
|
+
if (meta.dense) h += meta.height || 0;
|
|
45
|
+
else h += meta.height || 0;
|
|
46
|
+
}
|
|
47
|
+
return h;
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const bottomOffset = computed(() => {
|
|
51
|
+
let h = 0;
|
|
52
|
+
for (const [, meta] of app.footers) h += meta.height || 0;
|
|
53
|
+
return h;
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// For left/right drawer offset use only permanent drawers
|
|
57
|
+
const horizontalOffset = computed(() => {
|
|
58
|
+
let left = 0,
|
|
59
|
+
right = 0;
|
|
60
|
+
|
|
61
|
+
// console.log("App drawers:", toRaw(app.drawers));
|
|
62
|
+
for (const [, meta] of app.drawers) {
|
|
63
|
+
|
|
64
|
+
if (meta.mode === "permanent") {
|
|
65
|
+
// console.log("Drawer meta:", toRaw(meta));
|
|
66
|
+
if (meta.right) right += meta.width || 0;
|
|
67
|
+
else left += meta.width || 0;
|
|
68
|
+
}
|
|
69
|
+
// rail with expanded state contributes width when expanded
|
|
70
|
+
if (meta.mode === "rail" && meta.expanded) {
|
|
71
|
+
if (meta.right) right += meta.width || 0;
|
|
72
|
+
else left += meta.width || 0;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
// console.log("Raw app contents:", toRaw(app));
|
|
76
|
+
// console.log("horizontalOffset", toRaw(app.drawers), left, right);
|
|
77
|
+
return { left, right };
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
provide("layout", {
|
|
81
|
+
register,
|
|
82
|
+
unregister,
|
|
83
|
+
topOffset,
|
|
84
|
+
bottomOffset,
|
|
85
|
+
horizontalOffset,
|
|
86
|
+
app,
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
const rtlClass = computed(() =>
|
|
90
|
+
globalRtl.value ? "direction-rtl" : "direction-ltr"
|
|
91
|
+
);
|
|
92
|
+
</script>
|
|
93
|
+
|
|
94
|
+
<style>
|
|
95
|
+
/* small helpers in case Tailwind is insufficient */
|
|
96
|
+
.direction-rtl {
|
|
97
|
+
direction: rtl;
|
|
98
|
+
}
|
|
99
|
+
.direction-ltr {
|
|
100
|
+
direction: ltr;
|
|
101
|
+
}
|
|
102
|
+
</style>
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div :class="[bg, 'flex items-center justify-between px-2']">
|
|
3
|
+
<!-- Items per page and info -->
|
|
4
|
+
<bdi class="flex items-center">
|
|
5
|
+
<div class="flex items-center gap-2">
|
|
6
|
+
<p :class="['text-sm font-medium', textColor]">{{ t('pagination.itemsPerPage') }}</p>
|
|
7
|
+
<select
|
|
8
|
+
:value="itemsPerPage"
|
|
9
|
+
:class="[
|
|
10
|
+
'h-8 w-[70px] border bg-transparent px-2 py-1 text-sm',
|
|
11
|
+
'focus:outline-none focus:ring-2 focus:ring-offset-2',
|
|
12
|
+
inputBorder,
|
|
13
|
+
rounded
|
|
14
|
+
]"
|
|
15
|
+
@change="handleItemsPerPageChange"
|
|
16
|
+
>
|
|
17
|
+
<option v-for="option in itemsPerPageOptions" :key="option" :value="option">
|
|
18
|
+
{{ option }}
|
|
19
|
+
</option>
|
|
20
|
+
</select>
|
|
21
|
+
</div>
|
|
22
|
+
</bdi>
|
|
23
|
+
|
|
24
|
+
<div :class="['flex items-center gap-2 text-base font-medium', textColor]">
|
|
25
|
+
<bdi :class="language == 'dv' ? 'text-right faruma' : 'text-left'">{{ startItem }}-{{ endItem }} {{ t('pagination.of') }} {{ itemsLength }}</bdi>
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
<!-- Pagination controls -->
|
|
29
|
+
<bdi class="flex items-center gap-1">
|
|
30
|
+
<!-- First Page -->
|
|
31
|
+
<button
|
|
32
|
+
:disabled="page <= 1"
|
|
33
|
+
:class="[
|
|
34
|
+
'inline-flex items-center justify-center h-8 w-8 text-sm font-medium transition-colors',
|
|
35
|
+
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2',
|
|
36
|
+
'disabled:pointer-events-none disabled:opacity-50',
|
|
37
|
+
buttonSecondary,
|
|
38
|
+
rounded
|
|
39
|
+
]"
|
|
40
|
+
@click="handlePageChange(1)"
|
|
41
|
+
:title="t('pagination.page') + ' 1'"
|
|
42
|
+
>
|
|
43
|
+
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
44
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 19l-7-7 7-7m8 14l-7-7 7-7" />
|
|
45
|
+
</svg>
|
|
46
|
+
</button>
|
|
47
|
+
|
|
48
|
+
<!-- Previous Page -->
|
|
49
|
+
<button
|
|
50
|
+
:disabled="page <= 1"
|
|
51
|
+
:class="[
|
|
52
|
+
'inline-flex items-center justify-center h-8 w-8 text-sm font-medium transition-colors',
|
|
53
|
+
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2',
|
|
54
|
+
'disabled:pointer-events-none disabled:opacity-50',
|
|
55
|
+
buttonSecondary,
|
|
56
|
+
rounded
|
|
57
|
+
]"
|
|
58
|
+
@click="handlePageChange(page - 1)"
|
|
59
|
+
:title="t('pagination.previous')"
|
|
60
|
+
>
|
|
61
|
+
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
62
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
|
|
63
|
+
</svg>
|
|
64
|
+
</button>
|
|
65
|
+
|
|
66
|
+
<!-- Page Numbers -->
|
|
67
|
+
<div class="flex items-center gap-1">
|
|
68
|
+
<button
|
|
69
|
+
v-for="pageNum in visiblePages"
|
|
70
|
+
:key="pageNum"
|
|
71
|
+
:disabled="pageNum === '...'"
|
|
72
|
+
:class="[
|
|
73
|
+
'inline-flex items-center justify-center text-[#fff] h-8 min-w-8 px-3 text-sm font-medium transition-colors',
|
|
74
|
+
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2',
|
|
75
|
+
pageNum === '...' ? 'cursor-default pointer-events-none' : '',
|
|
76
|
+
pageNum === page ? buttonPrimary : buttonSecondary,
|
|
77
|
+
rounded
|
|
78
|
+
]"
|
|
79
|
+
@click="pageNum !== '...' && handlePageChange(pageNum)"
|
|
80
|
+
:title="pageNum !== '...' ? t('pagination.page') + ' ' + pageNum : ''"
|
|
81
|
+
>
|
|
82
|
+
{{ pageNum }}
|
|
83
|
+
</button>
|
|
84
|
+
</div>
|
|
85
|
+
|
|
86
|
+
<!-- Next Page -->
|
|
87
|
+
<button
|
|
88
|
+
:disabled="page >= totalPages"
|
|
89
|
+
:class="[
|
|
90
|
+
'inline-flex items-center justify-center h-8 w-8 text-sm font-medium transition-colors',
|
|
91
|
+
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2',
|
|
92
|
+
'disabled:pointer-events-none disabled:opacity-50',
|
|
93
|
+
buttonSecondary,
|
|
94
|
+
rounded
|
|
95
|
+
]"
|
|
96
|
+
@click="handlePageChange(page + 1)"
|
|
97
|
+
:title="t('pagination.next')"
|
|
98
|
+
>
|
|
99
|
+
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
100
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
|
|
101
|
+
</svg>
|
|
102
|
+
</button>
|
|
103
|
+
|
|
104
|
+
<!-- Last Page -->
|
|
105
|
+
<button
|
|
106
|
+
:disabled="page >= totalPages"
|
|
107
|
+
:class="[
|
|
108
|
+
'inline-flex items-center justify-center h-8 w-8 text-sm font-medium transition-colors',
|
|
109
|
+
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2',
|
|
110
|
+
'disabled:pointer-events-none disabled:opacity-50',
|
|
111
|
+
buttonSecondary,
|
|
112
|
+
rounded
|
|
113
|
+
]"
|
|
114
|
+
@click="handlePageChange(totalPages)"
|
|
115
|
+
:title="t('pagination.page') + ' ' + totalPages"
|
|
116
|
+
>
|
|
117
|
+
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
118
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 5l7 7-7 7M5 5l7 7-7 7" />
|
|
119
|
+
</svg>
|
|
120
|
+
</button>
|
|
121
|
+
</bdi>
|
|
122
|
+
</div>
|
|
123
|
+
</template>
|
|
124
|
+
|
|
125
|
+
<script setup>
|
|
126
|
+
import { computed, inject } from 'vue'
|
|
127
|
+
|
|
128
|
+
const { t, language } = inject('i18n')
|
|
129
|
+
|
|
130
|
+
const props = defineProps({
|
|
131
|
+
// Pagination data
|
|
132
|
+
page: {
|
|
133
|
+
type: Number,
|
|
134
|
+
required: true
|
|
135
|
+
},
|
|
136
|
+
itemsPerPage: {
|
|
137
|
+
type: Number,
|
|
138
|
+
required: true
|
|
139
|
+
},
|
|
140
|
+
itemsLength: {
|
|
141
|
+
type: Number,
|
|
142
|
+
required: true
|
|
143
|
+
},
|
|
144
|
+
itemsPerPageOptions: {
|
|
145
|
+
type: Array,
|
|
146
|
+
default: () => [10, 20, 30, 40, 50]
|
|
147
|
+
},
|
|
148
|
+
|
|
149
|
+
// Styling
|
|
150
|
+
bg: {
|
|
151
|
+
type: String,
|
|
152
|
+
default: 'bg-background'
|
|
153
|
+
},
|
|
154
|
+
border: {
|
|
155
|
+
type: String,
|
|
156
|
+
default: 'border-t border-input-border'
|
|
157
|
+
},
|
|
158
|
+
textColor: {
|
|
159
|
+
type: String,
|
|
160
|
+
default: 'text-textcolor'
|
|
161
|
+
},
|
|
162
|
+
inputBorder: {
|
|
163
|
+
type: String,
|
|
164
|
+
default: 'border-input-border focus:none'
|
|
165
|
+
},
|
|
166
|
+
buttonPrimary: {
|
|
167
|
+
type: String,
|
|
168
|
+
default: 'bg-primary hover:bg-primary/90'
|
|
169
|
+
},
|
|
170
|
+
buttonSecondary: {
|
|
171
|
+
type: String,
|
|
172
|
+
default: 'border border-input-border bg-background hover:bg-accent hover:text-accent-foreground'
|
|
173
|
+
},
|
|
174
|
+
rounded: {
|
|
175
|
+
type: String,
|
|
176
|
+
default: 'rounded-none'
|
|
177
|
+
}
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
const emit = defineEmits(['update:page', 'update:itemsPerPage', 'change'])
|
|
181
|
+
|
|
182
|
+
// Computed properties
|
|
183
|
+
const totalPages = computed(() => {
|
|
184
|
+
return Math.ceil(props.itemsLength / props.itemsPerPage) || 1
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
const startItem = computed(() => {
|
|
188
|
+
if (props.itemsLength === 0) return 0
|
|
189
|
+
return (props.page - 1) * props.itemsPerPage + 1
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
const endItem = computed(() => {
|
|
193
|
+
const end = props.page * props.itemsPerPage
|
|
194
|
+
return Math.min(end, props.itemsLength)
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
const visiblePages = computed(() => {
|
|
198
|
+
const current = props.page
|
|
199
|
+
const total = totalPages.value
|
|
200
|
+
const delta = 1 // Show 1 page on each side
|
|
201
|
+
const range = []
|
|
202
|
+
|
|
203
|
+
for (let i = Math.max(2, current - delta); i <= Math.min(total - 1, current + delta); i++) {
|
|
204
|
+
range.push(i)
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (current - delta > 2) {
|
|
208
|
+
range.unshift('...')
|
|
209
|
+
}
|
|
210
|
+
if (current + delta < total - 1) {
|
|
211
|
+
range.push('...')
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
range.unshift(1)
|
|
215
|
+
if (total > 1) {
|
|
216
|
+
range.push(total)
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return range.filter((item, index, arr) => arr.indexOf(item) === index)
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
// Methods
|
|
223
|
+
const handlePageChange = (newPage) => {
|
|
224
|
+
if (newPage >= 1 && newPage <= totalPages.value && newPage !== props.page) {
|
|
225
|
+
emit('update:page', newPage)
|
|
226
|
+
emit('change', {
|
|
227
|
+
page: newPage,
|
|
228
|
+
itemsPerPage: props.itemsPerPage
|
|
229
|
+
})
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const handleItemsPerPageChange = (event) => {
|
|
234
|
+
const newItemsPerPage = parseInt(event.target.value)
|
|
235
|
+
emit('update:itemsPerPage', newItemsPerPage)
|
|
236
|
+
emit('update:page', 1) // Reset to first page
|
|
237
|
+
emit('change', {
|
|
238
|
+
page: 1,
|
|
239
|
+
itemsPerPage: newItemsPerPage
|
|
240
|
+
})
|
|
241
|
+
}
|
|
242
|
+
</script>
|