mgv-backoffice 1.36.2 → 1.37.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 +32 -0
- package/dist/components/BaseDateTimePicker.vue.d.ts +49 -0
- package/dist/index.d.ts +2 -0
- package/dist/ui-lib.css +1 -1
- package/dist/ui-lib.js +864 -818
- package/dist/ui-lib.umd.cjs +1 -1
- package/package.json +17 -13
- package/src/components/BaseDateTimePicker.test.ts +49 -0
- package/src/components/BaseDateTimePicker.vue +91 -0
- package/src/components/BaseSidebar.vue +1 -1
- package/src/index.ts +4 -0
- package/src/style.css +58 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mgv-backoffice",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.37.0",
|
|
4
4
|
"description": "Shared Vue 3 UI component library",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/ui-lib.umd.cjs",
|
|
@@ -37,18 +37,22 @@
|
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"@heroicons/vue": "^2.2.0",
|
|
40
|
-
"@tailwindcss/vite": "^4.3.
|
|
41
|
-
"@
|
|
42
|
-
"@
|
|
43
|
-
"@vue/
|
|
40
|
+
"@tailwindcss/vite": "^4.3.3",
|
|
41
|
+
"@typescript/typescript6": "^6.0.2",
|
|
42
|
+
"@vitejs/plugin-vue": "^6.0.9",
|
|
43
|
+
"@vue/language-core": "^3.3.11",
|
|
44
|
+
"@vue/test-utils": "^2.5.1",
|
|
44
45
|
"jsdom": "^29.1.1",
|
|
45
|
-
"sass": "^1.
|
|
46
|
-
"tailwindcss": "^4.3.
|
|
47
|
-
"typescript": "^
|
|
48
|
-
"vite": "^8.1
|
|
49
|
-
"vite-plugin-dts": "^5.
|
|
50
|
-
"vitest": "^
|
|
51
|
-
"vue": "^3.5.
|
|
52
|
-
"vue-router": "^5.1
|
|
46
|
+
"sass": "^1.105.0",
|
|
47
|
+
"tailwindcss": "^4.3.3",
|
|
48
|
+
"typescript": "^7.0.2",
|
|
49
|
+
"vite": "^8.3.1",
|
|
50
|
+
"vite-plugin-dts": "^5.1.1",
|
|
51
|
+
"vitest": "^5.0.2",
|
|
52
|
+
"vue": "^3.5.43",
|
|
53
|
+
"vue-router": "^5.3.1"
|
|
54
|
+
},
|
|
55
|
+
"dependencies": {
|
|
56
|
+
"@vuepic/vue-datepicker": "^14.0.0"
|
|
53
57
|
}
|
|
54
58
|
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { mount } from '@vue/test-utils'
|
|
3
|
+
import { VueDatePicker } from '@vuepic/vue-datepicker'
|
|
4
|
+
import BaseDateTimePicker from './BaseDateTimePicker.vue'
|
|
5
|
+
|
|
6
|
+
function picker(props: Record<string, unknown> = {}, attrs: Record<string, unknown> = {}) {
|
|
7
|
+
return mount(BaseDateTimePicker, { props, attrs }).findComponent(VueDatePicker)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
describe('BaseDateTimePicker', () => {
|
|
11
|
+
it('defaults to date + 24h time, teleported, with the dd/MM/yyyy HH:mm format', () => {
|
|
12
|
+
const dp = picker()
|
|
13
|
+
expect(dp.props('timePicker')).toBe(false)
|
|
14
|
+
expect(dp.props('timeConfig')).toMatchObject({ enableTimePicker: true, is24: true })
|
|
15
|
+
expect(dp.props('formats')).toEqual({ input: 'dd/MM/yyyy HH:mm' })
|
|
16
|
+
expect(dp.props('teleport')).toBe(true)
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
it("mode 'date' hides the time picker", () => {
|
|
20
|
+
const dp = picker({ mode: 'date' })
|
|
21
|
+
expect(dp.props('timeConfig')).toMatchObject({ enableTimePicker: false })
|
|
22
|
+
expect(dp.props('formats')).toEqual({ input: 'dd/MM/yyyy' })
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
it("mode 'time' switches to the time-only picker, 12h when is24=false", () => {
|
|
26
|
+
const dp = picker({ mode: 'time', is24: false })
|
|
27
|
+
expect(dp.props('timePicker')).toBe(true)
|
|
28
|
+
expect(dp.props('formats')).toEqual({ input: 'hh:mm a' })
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it('custom format and timeConfig override the defaults', () => {
|
|
32
|
+
const dp = picker({ format: 'yyyy-MM-dd', timeConfig: { enableSeconds: true } })
|
|
33
|
+
expect(dp.props('formats')).toEqual({ input: 'yyyy-MM-dd' })
|
|
34
|
+
expect(dp.props('timeConfig')).toMatchObject({ enableTimePicker: true, enableSeconds: true })
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it('passes unknown props through to VueDatePicker', () => {
|
|
38
|
+
const dp = picker({}, { range: true, placeholder: 'Pick a date' })
|
|
39
|
+
expect(dp.props('range')).toBe(true)
|
|
40
|
+
expect(dp.props('placeholder')).toBe('Pick a date')
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it('re-emits update:modelValue (v-model contract)', async () => {
|
|
44
|
+
const wrapper = mount(BaseDateTimePicker)
|
|
45
|
+
const date = new Date(2026, 8, 27, 10, 30)
|
|
46
|
+
wrapper.findComponent(VueDatePicker).vm.$emit('update:model-value', date)
|
|
47
|
+
expect(wrapper.emitted('update:modelValue')).toEqual([[date]])
|
|
48
|
+
})
|
|
49
|
+
})
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<!--
|
|
3
|
+
Thin wrapper around @vuepic/vue-datepicker so every consumer gets the same
|
|
4
|
+
picker, the same defaults and the lib's emerald/slate skin (see the
|
|
5
|
+
`.dp--theme-*` overrides in style.css) without installing or wiring
|
|
6
|
+
anything themselves.
|
|
7
|
+
|
|
8
|
+
Only the common knobs are typed props here. Every other VueDatePicker
|
|
9
|
+
prop (range, min-date, max-date, disabled-dates, locale, …), event and
|
|
10
|
+
slot falls through untouched, so nothing the underlying picker can do is
|
|
11
|
+
lost.
|
|
12
|
+
|
|
13
|
+
Dark mode follows useTheme() automatically.
|
|
14
|
+
-->
|
|
15
|
+
<VueDatePicker
|
|
16
|
+
:model-value="modelValue"
|
|
17
|
+
:dark="isDark"
|
|
18
|
+
:time-picker="mode === 'time'"
|
|
19
|
+
:time-config="resolvedTimeConfig"
|
|
20
|
+
:formats="resolvedFormats"
|
|
21
|
+
:auto-apply="autoApply"
|
|
22
|
+
:teleport="teleport"
|
|
23
|
+
@update:model-value="emit('update:modelValue', $event)"
|
|
24
|
+
>
|
|
25
|
+
<template v-for="(_, name) in $slots" #[name]="slotProps">
|
|
26
|
+
<slot :name="name" v-bind="slotProps ?? {}" />
|
|
27
|
+
</template>
|
|
28
|
+
</VueDatePicker>
|
|
29
|
+
</template>
|
|
30
|
+
|
|
31
|
+
<script setup lang="ts">
|
|
32
|
+
import { computed } from 'vue'
|
|
33
|
+
import { VueDatePicker, type ModelValue, type TimeConfig } from '@vuepic/vue-datepicker'
|
|
34
|
+
import '@vuepic/vue-datepicker/dist/main.css'
|
|
35
|
+
import { useTheme } from '../composables/useTheme'
|
|
36
|
+
|
|
37
|
+
export type DateTimePickerMode = 'date' | 'datetime' | 'time'
|
|
38
|
+
|
|
39
|
+
interface Props {
|
|
40
|
+
/** v-model value — Date, Date[] (range), time object, or string with `model-type`. */
|
|
41
|
+
modelValue?: ModelValue
|
|
42
|
+
/** 'date' = calendar only; 'datetime' = calendar + time (default); 'time' = time only. */
|
|
43
|
+
mode?: DateTimePickerMode
|
|
44
|
+
/**
|
|
45
|
+
* date-fns pattern for the input text. Defaults per mode:
|
|
46
|
+
* 'dd/MM/yyyy', 'dd/MM/yyyy HH:mm' (12h: 'hh:mm a'), 'HH:mm'.
|
|
47
|
+
*/
|
|
48
|
+
format?: string
|
|
49
|
+
/** 24-hour clock. */
|
|
50
|
+
is24?: boolean
|
|
51
|
+
/** Select on click without the Cancel/Select action row. */
|
|
52
|
+
autoApply?: boolean
|
|
53
|
+
/**
|
|
54
|
+
* Where the menu renders. Defaults to `true` (body) so it isn't clipped by
|
|
55
|
+
* modals or overflow-hidden containers.
|
|
56
|
+
*/
|
|
57
|
+
teleport?: boolean | string | HTMLElement
|
|
58
|
+
/** Extra time options, merged over the mode-derived defaults. */
|
|
59
|
+
timeConfig?: Partial<TimeConfig>
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
63
|
+
modelValue: null,
|
|
64
|
+
mode: 'datetime',
|
|
65
|
+
format: undefined,
|
|
66
|
+
is24: true,
|
|
67
|
+
autoApply: false,
|
|
68
|
+
teleport: true,
|
|
69
|
+
timeConfig: undefined,
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
const emit = defineEmits<{
|
|
73
|
+
(e: 'update:modelValue', value: ModelValue): void
|
|
74
|
+
}>()
|
|
75
|
+
|
|
76
|
+
const { isDark } = useTheme()
|
|
77
|
+
|
|
78
|
+
const resolvedTimeConfig = computed<Partial<TimeConfig>>(() => ({
|
|
79
|
+
enableTimePicker: props.mode !== 'date',
|
|
80
|
+
is24: props.is24,
|
|
81
|
+
...props.timeConfig,
|
|
82
|
+
}))
|
|
83
|
+
|
|
84
|
+
const resolvedFormats = computed(() => {
|
|
85
|
+
if (props.format) return { input: props.format }
|
|
86
|
+
const time = props.is24 ? 'HH:mm' : 'hh:mm a'
|
|
87
|
+
if (props.mode === 'date') return { input: 'dd/MM/yyyy' }
|
|
88
|
+
if (props.mode === 'time') return { input: time }
|
|
89
|
+
return { input: `dd/MM/yyyy ${time}` }
|
|
90
|
+
})
|
|
91
|
+
</script>
|
|
@@ -304,7 +304,7 @@ function navigate(name: string) {
|
|
|
304
304
|
class="flex items-center justify-center gap-2 text-xs"
|
|
305
305
|
:class="isDark ? 'text-gray-500' : 'text-gray-500'"
|
|
306
306
|
>
|
|
307
|
-
<span>{{ appName }}<template v-if="appName && version"> </template><template v-if="version">v{{ version }}</template></span>
|
|
307
|
+
<span>{{ appName }}<template v-if="appName && version">{{ ' ' }}</template><template v-if="version">v{{ version }}</template></span>
|
|
308
308
|
</div>
|
|
309
309
|
</slot>
|
|
310
310
|
</div>
|
package/src/index.ts
CHANGED
|
@@ -53,6 +53,10 @@ export { default as BasePillPickerModal } from './components/BasePillPickerModal
|
|
|
53
53
|
export { default as BaseBarDistribution } from './components/BaseBarDistribution.vue'
|
|
54
54
|
export type { CredentialsView, CredentialsUpdate } from './components/BaseCredentialsForm.vue'
|
|
55
55
|
|
|
56
|
+
// Components — third-party wrappers
|
|
57
|
+
export { default as BaseDateTimePicker } from './components/BaseDateTimePicker.vue'
|
|
58
|
+
export type { DateTimePickerMode } from './components/BaseDateTimePicker.vue'
|
|
59
|
+
|
|
56
60
|
// Composables
|
|
57
61
|
export { useTheme, initTheme } from './composables/useTheme'
|
|
58
62
|
export type { UseThemeOptions } from './composables/useTheme'
|
package/src/style.css
CHANGED
|
@@ -30,3 +30,61 @@
|
|
|
30
30
|
body {
|
|
31
31
|
font-family: var(--font-sans);
|
|
32
32
|
}
|
|
33
|
+
|
|
34
|
+
/* BaseDateTimePicker skin: re-point @vuepic/vue-datepicker's CSS variables at
|
|
35
|
+
the lib palette (emerald accent, slate surfaces — same as BaseInput). The
|
|
36
|
+
picker is third-party markup, so its variables are the only styling hook;
|
|
37
|
+
Tailwind utilities can't reach inside it. `html` prefix out-specifies the
|
|
38
|
+
package's own `.dp--theme-*` blocks regardless of stylesheet order. */
|
|
39
|
+
html .dp--theme-light,
|
|
40
|
+
html .dp--theme-dark {
|
|
41
|
+
--dp-font-family: var(--font-sans);
|
|
42
|
+
--dp-font-size: 0.875rem;
|
|
43
|
+
--dp-border-radius: 0.25rem;
|
|
44
|
+
--dp-cell-border-radius: 0.25rem;
|
|
45
|
+
--dp-input-padding: 8px 30px 8px 12px;
|
|
46
|
+
--dp-primary-text-color: #fff;
|
|
47
|
+
}
|
|
48
|
+
html .dp--theme-light {
|
|
49
|
+
--dp-background-color: #f8fafc; /* slate-50 */
|
|
50
|
+
--dp-text-color: #0f172a; /* slate-900 */
|
|
51
|
+
--dp-hover-color: #f1f5f9; /* slate-100 */
|
|
52
|
+
--dp-hover-text-color: #0f172a;
|
|
53
|
+
--dp-hover-icon-color: #64748b; /* slate-500 */
|
|
54
|
+
--dp-icon-color: #94a3b8; /* slate-400 */
|
|
55
|
+
--dp-primary-color: #059669; /* emerald-600 */
|
|
56
|
+
--dp-primary-disabled-color: #6ee7b7; /* emerald-300 */
|
|
57
|
+
--dp-secondary-color: #94a3b8;
|
|
58
|
+
--dp-border-color: #cbd5e1; /* slate-300 */
|
|
59
|
+
--dp-menu-border-color: #cbd5e1;
|
|
60
|
+
--dp-border-color-hover: #94a3b8;
|
|
61
|
+
--dp-border-color-focus: #10b981; /* emerald-500 */
|
|
62
|
+
--dp-highlight-color: rgb(16 185 129 / 0.12);
|
|
63
|
+
--dp-success-color: #10b981;
|
|
64
|
+
--dp-danger-color: #ef4444; /* red-500 */
|
|
65
|
+
--dp-marker-color: #ef4444;
|
|
66
|
+
}
|
|
67
|
+
html .dp--theme-dark {
|
|
68
|
+
--dp-background-color: #0f172a; /* slate-900 */
|
|
69
|
+
--dp-text-color: #f1f5f9; /* slate-100 */
|
|
70
|
+
--dp-hover-color: #1e293b; /* slate-800 */
|
|
71
|
+
--dp-hover-text-color: #f1f5f9;
|
|
72
|
+
--dp-hover-icon-color: #cbd5e1; /* slate-300 */
|
|
73
|
+
--dp-icon-color: #64748b; /* slate-500 */
|
|
74
|
+
--dp-primary-color: #059669; /* emerald-600 */
|
|
75
|
+
--dp-primary-disabled-color: #065f46; /* emerald-800 */
|
|
76
|
+
--dp-secondary-color: #64748b;
|
|
77
|
+
--dp-border-color: #475569; /* slate-600 */
|
|
78
|
+
--dp-menu-border-color: #334155; /* slate-700 */
|
|
79
|
+
--dp-border-color-hover: #64748b;
|
|
80
|
+
--dp-border-color-focus: #10b981; /* emerald-500 */
|
|
81
|
+
--dp-highlight-color: rgb(16 185 129 / 0.2);
|
|
82
|
+
--dp-disabled-color: #1e293b;
|
|
83
|
+
--dp-disabled-color-text: #64748b;
|
|
84
|
+
--dp-scroll-bar-background: #0f172a;
|
|
85
|
+
--dp-scroll-bar-color: #334155;
|
|
86
|
+
--dp-success-color: #10b981;
|
|
87
|
+
--dp-danger-color: #ef4444;
|
|
88
|
+
--dp-marker-color: #ef4444;
|
|
89
|
+
--dp-tooltip-color: #1e293b;
|
|
90
|
+
}
|