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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mgv-backoffice",
3
- "version": "1.36.2",
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.1",
41
- "@vitejs/plugin-vue": "^6.0.7",
42
- "@vue/language-core": "^3.3.5",
43
- "@vue/test-utils": "^2.4.11",
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.101.0",
46
- "tailwindcss": "^4.3.1",
47
- "typescript": "^6.0.3",
48
- "vite": "^8.1.0",
49
- "vite-plugin-dts": "^5.0.3",
50
- "vitest": "^4.1.9",
51
- "vue": "^3.5.38",
52
- "vue-router": "^5.1.0"
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
+ }