@mapomodule/form 2.0.0-beta.1 → 2.0.0-beta.11

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.
Files changed (202) hide show
  1. package/README.md +37 -118
  2. package/dist/__tests__/__mocks__/nuxt-imports.d.ts +4 -0
  3. package/dist/__tests__/__mocks__/nuxt-imports.d.ts.map +1 -0
  4. package/dist/__tests__/camomillaRoundTrip.test.d.ts +2 -0
  5. package/dist/__tests__/camomillaRoundTrip.test.d.ts.map +1 -0
  6. package/dist/__tests__/registry.test.d.ts +2 -0
  7. package/dist/__tests__/registry.test.d.ts.map +1 -0
  8. package/dist/__tests__/tabVisibility.test.d.ts +2 -0
  9. package/dist/__tests__/tabVisibility.test.d.ts.map +1 -0
  10. package/dist/__tests__/useFormFromSchema.test.d.ts +2 -0
  11. package/dist/__tests__/useFormFromSchema.test.d.ts.map +1 -0
  12. package/dist/__tests__/useMapoForm.test.d.ts +2 -0
  13. package/dist/__tests__/useMapoForm.test.d.ts.map +1 -0
  14. package/dist/index.d.mts +9 -1
  15. package/dist/index.d.ts +15 -1
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.mjs +7 -34
  18. package/dist/module.d.mts +20 -2
  19. package/dist/module.d.ts +12 -2
  20. package/dist/module.d.ts.map +1 -0
  21. package/dist/module.json +1 -1
  22. package/dist/module.mjs +187 -13
  23. package/dist/runtime/components/MapoFocusPortal.vue +115 -0
  24. package/dist/runtime/components/MapoFocusPortal.vue.d.ts +4 -0
  25. package/dist/runtime/components/MapoFocusPortal.vue.d.ts.map +1 -0
  26. package/dist/runtime/components/MapoForm.vue +177 -0
  27. package/dist/runtime/components/MapoForm.vue.d.ts +47 -0
  28. package/dist/runtime/components/MapoForm.vue.d.ts.map +1 -0
  29. package/dist/runtime/components/MapoFormField.vue +269 -0
  30. package/dist/runtime/components/MapoFormField.vue.d.ts +46 -0
  31. package/dist/runtime/components/MapoFormField.vue.d.ts.map +1 -0
  32. package/dist/runtime/components/MapoFormFlatSection.vue +149 -0
  33. package/dist/runtime/components/MapoFormFlatSection.vue.d.ts +15 -0
  34. package/dist/runtime/components/MapoFormFlatSection.vue.d.ts.map +1 -0
  35. package/dist/runtime/components/MapoFormGroup.vue +247 -0
  36. package/dist/runtime/components/MapoFormGroup.vue.d.ts +44 -0
  37. package/dist/runtime/components/MapoFormGroup.vue.d.ts.map +1 -0
  38. package/dist/runtime/components/MapoFormTabs.vue +122 -0
  39. package/dist/runtime/components/MapoFormTabs.vue.d.ts +15 -0
  40. package/dist/runtime/components/MapoFormTabs.vue.d.ts.map +1 -0
  41. package/dist/runtime/components/MapoUnknownField.vue +62 -0
  42. package/dist/runtime/components/MapoUnknownField.vue.d.ts +16 -0
  43. package/dist/runtime/components/MapoUnknownField.vue.d.ts.map +1 -0
  44. package/dist/runtime/components/fields/MapoDateField.vue +88 -0
  45. package/dist/runtime/components/fields/MapoDateField.vue.d.ts +17 -0
  46. package/dist/runtime/components/fields/MapoDateField.vue.d.ts.map +1 -0
  47. package/dist/runtime/components/fields/MapoDateTimeField.vue +159 -0
  48. package/dist/runtime/components/fields/MapoDateTimeField.vue.d.ts +17 -0
  49. package/dist/runtime/components/fields/MapoDateTimeField.vue.d.ts.map +1 -0
  50. package/dist/runtime/components/fields/MapoEnhancedMediaField.vue +130 -0
  51. package/dist/runtime/components/fields/MapoEnhancedMediaField.vue.d.ts +16 -0
  52. package/dist/runtime/components/fields/MapoEnhancedMediaField.vue.d.ts.map +1 -0
  53. package/dist/runtime/components/fields/MapoFileField.vue +99 -0
  54. package/dist/runtime/components/fields/MapoFileField.vue.d.ts +16 -0
  55. package/dist/runtime/components/fields/MapoFileField.vue.d.ts.map +1 -0
  56. package/dist/runtime/components/fields/MapoFksField.vue +122 -0
  57. package/dist/runtime/components/fields/MapoFksField.vue.d.ts +16 -0
  58. package/dist/runtime/components/fields/MapoFksField.vue.d.ts.map +1 -0
  59. package/dist/runtime/components/fields/MapoMapField.vue +99 -0
  60. package/dist/runtime/components/fields/MapoMapField.vue.d.ts +22 -0
  61. package/dist/runtime/components/fields/MapoMapField.vue.d.ts.map +1 -0
  62. package/dist/runtime/components/fields/MapoMapFieldClient.vue +67 -0
  63. package/dist/runtime/components/fields/MapoMapFieldClient.vue.d.ts +20 -0
  64. package/dist/runtime/components/fields/MapoMapFieldClient.vue.d.ts.map +1 -0
  65. package/dist/runtime/components/fields/MapoMediaField.vue +93 -0
  66. package/dist/runtime/components/fields/MapoMediaField.vue.d.ts +16 -0
  67. package/dist/runtime/components/fields/MapoMediaField.vue.d.ts.map +1 -0
  68. package/dist/runtime/components/fields/MapoMediaM2mField.vue +111 -0
  69. package/dist/runtime/components/fields/MapoMediaM2mField.vue.d.ts +16 -0
  70. package/dist/runtime/components/fields/MapoMediaM2mField.vue.d.ts.map +1 -0
  71. package/dist/runtime/components/fields/MapoMediaUnavailable.vue +15 -0
  72. package/dist/runtime/components/fields/MapoMediaUnavailable.vue.d.ts +4 -0
  73. package/dist/runtime/components/fields/MapoMediaUnavailable.vue.d.ts.map +1 -0
  74. package/dist/runtime/components/fields/MapoRepeater.vue +459 -0
  75. package/dist/runtime/components/fields/MapoRepeater.vue.d.ts +16 -0
  76. package/dist/runtime/components/fields/MapoRepeater.vue.d.ts.map +1 -0
  77. package/dist/runtime/components/fields/MapoRepeaterItem.vue +346 -0
  78. package/dist/runtime/components/fields/MapoRepeaterItem.vue.d.ts +55 -0
  79. package/dist/runtime/components/fields/MapoRepeaterItem.vue.d.ts.map +1 -0
  80. package/dist/runtime/components/fields/MapoSeoPreview.vue +122 -0
  81. package/dist/runtime/components/fields/MapoSeoPreview.vue.d.ts +22 -0
  82. package/dist/runtime/components/fields/MapoSeoPreview.vue.d.ts.map +1 -0
  83. package/dist/runtime/components/fields/MapoTimeField.vue +44 -0
  84. package/dist/runtime/components/fields/MapoTimeField.vue.d.ts +17 -0
  85. package/dist/runtime/components/fields/MapoTimeField.vue.d.ts.map +1 -0
  86. package/dist/runtime/components/fields/MapoWygEditor.vue +258 -0
  87. package/dist/runtime/components/fields/MapoWygEditor.vue.d.ts +16 -0
  88. package/dist/runtime/components/fields/MapoWygEditor.vue.d.ts.map +1 -0
  89. package/dist/runtime/components/fields/nui/NuiCheckbox.vue +9 -0
  90. package/dist/runtime/components/fields/nui/NuiCheckbox.vue.d.ts +4 -0
  91. package/dist/runtime/components/fields/nui/NuiCheckbox.vue.d.ts.map +1 -0
  92. package/dist/runtime/components/fields/nui/NuiInput.vue +9 -0
  93. package/dist/runtime/components/fields/nui/NuiInput.vue.d.ts +4 -0
  94. package/dist/runtime/components/fields/nui/NuiInput.vue.d.ts.map +1 -0
  95. package/dist/runtime/components/fields/nui/NuiSelectMenu.vue +18 -0
  96. package/dist/runtime/components/fields/nui/NuiSelectMenu.vue.d.ts +4 -0
  97. package/dist/runtime/components/fields/nui/NuiSelectMenu.vue.d.ts.map +1 -0
  98. package/dist/runtime/components/fields/nui/NuiSlider.vue +9 -0
  99. package/dist/runtime/components/fields/nui/NuiSlider.vue.d.ts +4 -0
  100. package/dist/runtime/components/fields/nui/NuiSlider.vue.d.ts.map +1 -0
  101. package/dist/runtime/components/fields/nui/NuiSwitch.vue +9 -0
  102. package/dist/runtime/components/fields/nui/NuiSwitch.vue.d.ts +4 -0
  103. package/dist/runtime/components/fields/nui/NuiSwitch.vue.d.ts.map +1 -0
  104. package/dist/runtime/components/fields/nui/NuiTextarea.vue +9 -0
  105. package/dist/runtime/components/fields/nui/NuiTextarea.vue.d.ts +4 -0
  106. package/dist/runtime/components/fields/nui/NuiTextarea.vue.d.ts.map +1 -0
  107. package/dist/runtime/components/fields/nui/useNuiFieldAttrs.d.ts +13 -0
  108. package/dist/runtime/components/fields/nui/useNuiFieldAttrs.d.ts.map +1 -0
  109. package/dist/runtime/components/fields/nui/useNuiFieldAttrs.js +19 -0
  110. package/dist/runtime/composables/defineFormField.d.ts +25 -0
  111. package/dist/runtime/composables/defineFormField.d.ts.map +1 -0
  112. package/dist/runtime/composables/defineFormField.js +27 -0
  113. package/dist/runtime/composables/useCurrentLang.d.ts +6 -0
  114. package/dist/runtime/composables/useCurrentLang.d.ts.map +1 -0
  115. package/dist/runtime/composables/useCurrentLang.js +9 -0
  116. package/dist/runtime/composables/useFocusMode.d.ts +40 -0
  117. package/dist/runtime/composables/useFocusMode.d.ts.map +1 -0
  118. package/dist/runtime/composables/useFocusMode.js +31 -0
  119. package/dist/runtime/composables/useFormDraft.d.ts +60 -0
  120. package/dist/runtime/composables/useFormDraft.d.ts.map +1 -0
  121. package/dist/runtime/composables/useFormDraft.js +52 -0
  122. package/dist/runtime/composables/useFormFromSchema.d.ts +21 -0
  123. package/dist/runtime/composables/useFormFromSchema.d.ts.map +1 -0
  124. package/dist/runtime/composables/useFormFromSchema.js +145 -0
  125. package/dist/runtime/composables/useMapoForm.d.ts +96 -0
  126. package/dist/runtime/composables/useMapoForm.d.ts.map +1 -0
  127. package/dist/runtime/composables/useMapoForm.js +325 -0
  128. package/dist/runtime/composables/useMediaManager.d.ts +17 -0
  129. package/dist/runtime/composables/useMediaManager.d.ts.map +1 -0
  130. package/dist/runtime/composables/useMediaManager.js +6 -0
  131. package/dist/runtime/composables/useProgressiveDisclosure.d.ts +44 -0
  132. package/dist/runtime/composables/useProgressiveDisclosure.d.ts.map +1 -0
  133. package/dist/runtime/composables/useProgressiveDisclosure.js +23 -0
  134. package/dist/runtime/composables/useUnsavedChangesGuard.d.ts +24 -0
  135. package/dist/runtime/composables/useUnsavedChangesGuard.d.ts.map +1 -0
  136. package/dist/runtime/composables/useUnsavedChangesGuard.js +28 -0
  137. package/dist/runtime/devtools/DevtoolsFormsPage.vue +235 -0
  138. package/dist/runtime/devtools/DevtoolsFormsPage.vue.d.ts +4 -0
  139. package/dist/runtime/devtools/DevtoolsFormsPage.vue.d.ts.map +1 -0
  140. package/dist/runtime/plugins/form-registry.d.ts +18 -0
  141. package/dist/runtime/plugins/form-registry.d.ts.map +1 -0
  142. package/dist/runtime/plugins/form-registry.js +19 -0
  143. package/dist/runtime/registry/defaults.d.ts +3 -0
  144. package/dist/runtime/registry/defaults.d.ts.map +1 -0
  145. package/dist/runtime/registry/defaults.js +93 -0
  146. package/dist/runtime/types/descriptor.d.ts +373 -0
  147. package/dist/runtime/types/descriptor.d.ts.map +1 -0
  148. package/dist/runtime/types/descriptor.js +26 -0
  149. package/dist/runtime/types/index.d.ts +12 -0
  150. package/dist/runtime/types/index.d.ts.map +1 -0
  151. package/dist/runtime/types/index.js +6 -0
  152. package/dist/runtime/types/layout.d.ts +21 -0
  153. package/dist/runtime/types/layout.d.ts.map +1 -0
  154. package/dist/runtime/types/layout.js +0 -0
  155. package/dist/runtime/types/media.d.ts +43 -0
  156. package/dist/runtime/types/media.d.ts.map +1 -0
  157. package/dist/runtime/types/media.js +0 -0
  158. package/dist/runtime/types/registry.d.ts +62 -0
  159. package/dist/runtime/types/registry.d.ts.map +1 -0
  160. package/dist/runtime/types/registry.js +19 -0
  161. package/dist/runtime/utils/flattenFieldGroups.d.ts +50 -0
  162. package/dist/runtime/utils/flattenFieldGroups.d.ts.map +1 -0
  163. package/dist/runtime/utils/flattenFieldGroups.js +24 -0
  164. package/dist/runtime/utils/tabVisibility.d.ts +18 -0
  165. package/dist/runtime/utils/tabVisibility.d.ts.map +1 -0
  166. package/dist/runtime/utils/tabVisibility.js +18 -0
  167. package/dist/types.d.mts +6 -10
  168. package/package.json +10 -8
  169. package/src/__tests__/article.schema.json +193 -0
  170. package/src/__tests__/camomillaRoundTrip.test.ts +73 -0
  171. package/src/__tests__/tabVisibility.test.ts +102 -0
  172. package/src/__tests__/useFormFromSchema.test.ts +24 -0
  173. package/src/__tests__/useMapoForm.test.ts +5 -0
  174. package/src/index.ts +5 -0
  175. package/src/module.ts +34 -0
  176. package/src/runtime/components/MapoFocusPortal.vue +3 -1
  177. package/src/runtime/components/MapoForm.vue +17 -4
  178. package/src/runtime/components/MapoFormTabs.vue +29 -6
  179. package/src/runtime/components/fields/MapoDateField.vue +3 -1
  180. package/src/runtime/components/fields/MapoDateTimeField.vue +3 -1
  181. package/src/runtime/components/fields/MapoEnhancedMediaField.vue +153 -0
  182. package/src/runtime/components/fields/MapoFileField.vue +3 -1
  183. package/src/runtime/components/fields/MapoFksField.vue +7 -2
  184. package/src/runtime/components/fields/MapoMapField.vue +5 -2
  185. package/src/runtime/components/fields/MapoMediaField.vue +106 -0
  186. package/src/runtime/components/fields/MapoMediaM2mField.vue +131 -0
  187. package/src/runtime/components/fields/MapoMediaUnavailable.vue +19 -0
  188. package/src/runtime/components/fields/MapoRepeater.vue +33 -26
  189. package/src/runtime/components/fields/MapoRepeaterItem.vue +14 -8
  190. package/src/runtime/components/fields/MapoSeoPreview.vue +17 -13
  191. package/src/runtime/components/fields/MapoWygEditor.vue +55 -7
  192. package/src/runtime/composables/useFormFromSchema.ts +4 -1
  193. package/src/runtime/composables/useMapoForm.ts +4 -2
  194. package/src/runtime/composables/useMediaManager.ts +21 -0
  195. package/src/runtime/composables/useUnsavedChangesGuard.ts +3 -3
  196. package/src/runtime/registry/defaults.ts +9 -1
  197. package/src/runtime/types/descriptor.ts +22 -7
  198. package/src/runtime/types/index.ts +4 -0
  199. package/src/runtime/types/media.ts +44 -0
  200. package/src/runtime/utils/tabVisibility.ts +44 -0
  201. package/dist/index.d.cts +0 -1
  202. package/dist/module.d.cts +0 -2
@@ -0,0 +1,16 @@
1
+ import type { AnyFieldDescriptor } from "../types/index.js";
2
+ type __VLS_Props = {
3
+ modelValue: unknown;
4
+ descriptor: AnyFieldDescriptor;
5
+ errors?: string[];
6
+ readonly?: boolean;
7
+ disabled?: boolean;
8
+ };
9
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
10
+ "update:modelValue": (value: unknown) => any;
11
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
12
+ "onUpdate:modelValue"?: ((value: unknown) => any) | undefined;
13
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
14
+ declare const _default: typeof __VLS_export;
15
+ export default _default;
16
+ //# sourceMappingURL=MapoUnknownField.vue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MapoUnknownField.vue.d.ts","sourceRoot":"","sources":["../../../src/runtime/components/MapoUnknownField.vue"],"names":[],"mappings":"AA8EA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAE5D,KAAK,WAAW,GAAG;IACjB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,kBAAkB,CAAC;IAC/B,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AA6IF,QAAA,MAAM,YAAY;;;;kFAGhB,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}
@@ -0,0 +1,88 @@
1
+ <script setup>
2
+ import { computed, ref } from "vue";
3
+ import { useI18n } from "vue-i18n";
4
+ import { parseDate, CalendarDate } from "@internationalized/date";
5
+ const props = defineProps({
6
+ modelValue: { type: null, required: true },
7
+ descriptor: { type: Object, required: true },
8
+ errors: { type: Array, required: false },
9
+ readonly: { type: Boolean, required: false },
10
+ disabled: { type: Boolean, required: false }
11
+ });
12
+ const emit = defineEmits(["update:modelValue"]);
13
+ const { t } = useI18n();
14
+ const open = ref(false);
15
+ const calendarValue = computed(() => {
16
+ const v = props.modelValue;
17
+ if (!v) return void 0;
18
+ try {
19
+ return parseDate(String(v).split("T")[0]);
20
+ } catch {
21
+ return void 0;
22
+ }
23
+ });
24
+ const displayLabel = computed(() => {
25
+ const v = calendarValue.value;
26
+ if (!v) return t("mapo.dateField.selectDate");
27
+ return `${String(v.day).padStart(2, "0")}/${String(v.month).padStart(2, "0")}/${v.year}`;
28
+ });
29
+ const attrs = computed(() => props.descriptor.attrs ?? {});
30
+ function onDateSelect(date) {
31
+ const d = date;
32
+ open.value = false;
33
+ emit("update:modelValue", d ? d.toString() : null);
34
+ }
35
+ function clear() {
36
+ emit("update:modelValue", null);
37
+ open.value = false;
38
+ }
39
+ </script>
40
+
41
+ <template>
42
+ <UPopover v-model:open="open" :disabled="readonly || disabled">
43
+ <UButton
44
+ variant="outline"
45
+ color="neutral"
46
+ icon="i-lucide-calendar"
47
+ trailing
48
+ class="w-full justify-between font-normal"
49
+ :class="{
50
+ 'opacity-50 cursor-not-allowed': readonly || disabled,
51
+ 'ring-2 ring-red-500': errors?.length
52
+ }"
53
+ :disabled="readonly || disabled"
54
+ >
55
+ <span
56
+ :class="
57
+ calendarValue ? 'text-gray-900 dark:text-white' : 'text-gray-400'
58
+ "
59
+ >
60
+ {{ displayLabel }}
61
+ </span>
62
+ </UButton>
63
+
64
+ <template #content>
65
+ <div class="p-3 space-y-2">
66
+ <UCalendar
67
+ :model-value="calendarValue"
68
+ :min-value="attrs.min ? parseDate(attrs.min) : void 0"
69
+ :max-value="attrs.max ? parseDate(attrs.max) : void 0"
70
+ @update:model-value="onDateSelect"
71
+ />
72
+ <div
73
+ class="flex justify-end border-t border-gray-200 dark:border-gray-700 pt-2"
74
+ >
75
+ <UButton
76
+ variant="ghost"
77
+ color="neutral"
78
+ size="xs"
79
+ icon="i-lucide-x"
80
+ @click="clear"
81
+ >
82
+ Clear
83
+ </UButton>
84
+ </div>
85
+ </div>
86
+ </template>
87
+ </UPopover>
88
+ </template>
@@ -0,0 +1,17 @@
1
+ import type { DateDescriptor } from "../../types/index.js";
2
+ /** Date-only field backed by `UCalendar` and serialised as `YYYY-MM-DD`. */
3
+ type __VLS_Props = {
4
+ modelValue: unknown;
5
+ descriptor: DateDescriptor;
6
+ errors?: string[];
7
+ readonly?: boolean;
8
+ disabled?: boolean;
9
+ };
10
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
11
+ "update:modelValue": (value: string | null) => any;
12
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
13
+ "onUpdate:modelValue"?: ((value: string | null) => any) | undefined;
14
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
15
+ declare const _default: typeof __VLS_export;
16
+ export default _default;
17
+ //# sourceMappingURL=MapoDateField.vue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MapoDateField.vue.d.ts","sourceRoot":"","sources":["../../../../src/runtime/components/fields/MapoDateField.vue"],"names":[],"mappings":"AA0GA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D,4EAA4E;AAC5E,KAAK,WAAW,GAAG;IACjB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,cAAc,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAyMF,QAAA,MAAM,YAAY;;;;kFAGhB,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}
@@ -0,0 +1,159 @@
1
+ <script setup>
2
+ import { computed, ref } from "vue";
3
+ import {
4
+ parseDateTime,
5
+ CalendarDateTime,
6
+ today,
7
+ getLocalTimeZone
8
+ } from "@internationalized/date";
9
+ import { useI18n } from "vue-i18n";
10
+ const props = defineProps({
11
+ modelValue: { type: null, required: true },
12
+ descriptor: { type: Object, required: true },
13
+ errors: { type: Array, required: false },
14
+ readonly: { type: Boolean, required: false },
15
+ disabled: { type: Boolean, required: false }
16
+ });
17
+ const emit = defineEmits(["update:modelValue"]);
18
+ const { t } = useI18n();
19
+ const open = ref(false);
20
+ const tzMode = computed(() => props.descriptor.attrs?.tz ?? "naive");
21
+ const calendarValue = computed(() => {
22
+ const v = props.modelValue;
23
+ if (!v) return void 0;
24
+ try {
25
+ if (tzMode.value === "utc") {
26
+ const d = new Date(v);
27
+ if (Number.isNaN(d.getTime())) return void 0;
28
+ return new CalendarDateTime(
29
+ d.getFullYear(),
30
+ d.getMonth() + 1,
31
+ d.getDate(),
32
+ d.getHours(),
33
+ d.getMinutes(),
34
+ d.getSeconds()
35
+ );
36
+ }
37
+ const clean = v.replace(/Z$/, "").replace(/[+-]\d{2}:\d{2}$/, "").split(".")[0];
38
+ return parseDateTime(clean);
39
+ } catch {
40
+ return void 0;
41
+ }
42
+ });
43
+ function emitFromCalendar(v) {
44
+ if (tzMode.value === "utc") {
45
+ const d = new Date(v.year, v.month - 1, v.day, v.hour, v.minute, 0, 0);
46
+ emit("update:modelValue", d.toISOString());
47
+ return;
48
+ }
49
+ emit("update:modelValue", v.toString());
50
+ }
51
+ const displayLabel = computed(() => {
52
+ const v = calendarValue.value;
53
+ if (!v) return t("mapo.dateField.selectDateTime");
54
+ const date = `${String(v.day).padStart(2, "0")}/${String(v.month).padStart(2, "0")}/${v.year}`;
55
+ const time = `${String(v.hour).padStart(2, "0")}:${String(v.minute).padStart(2, "0")}`;
56
+ return `${date} ${time}`;
57
+ });
58
+ const timeHour = computed(() => calendarValue.value?.hour ?? 0);
59
+ const timeMinute = computed(() => calendarValue.value?.minute ?? 0);
60
+ function onDateSelect(date) {
61
+ const d = date;
62
+ if (!d) {
63
+ emit("update:modelValue", null);
64
+ return;
65
+ }
66
+ const h = calendarValue.value?.hour ?? 0;
67
+ const m = calendarValue.value?.minute ?? 0;
68
+ const merged = new CalendarDateTime(d.year, d.month, d.day, h, m);
69
+ emitFromCalendar(merged);
70
+ }
71
+ function onTimeChange(part, val) {
72
+ const n = Math.max(
73
+ 0,
74
+ Math.min(part === "hour" ? 23 : 59, parseInt(val) || 0)
75
+ );
76
+ const base = calendarValue.value ?? (() => {
77
+ const t2 = today(getLocalTimeZone());
78
+ return new CalendarDateTime(t2.year, t2.month, t2.day, 0, 0);
79
+ })();
80
+ const merged = part === "hour" ? new CalendarDateTime(base.year, base.month, base.day, n, base.minute) : new CalendarDateTime(base.year, base.month, base.day, base.hour, n);
81
+ emitFromCalendar(merged);
82
+ }
83
+ function clear() {
84
+ emit("update:modelValue", null);
85
+ open.value = false;
86
+ }
87
+ </script>
88
+
89
+ <template>
90
+ <UPopover v-model:open="open" :disabled="readonly || disabled">
91
+ <UButton
92
+ variant="outline"
93
+ color="neutral"
94
+ icon="i-lucide-calendar"
95
+ trailing
96
+ class="w-full justify-between font-normal"
97
+ :class="{
98
+ 'opacity-50 cursor-not-allowed': readonly || disabled,
99
+ 'ring-2 ring-red-500': errors?.length
100
+ }"
101
+ :disabled="readonly || disabled"
102
+ >
103
+ <span
104
+ :class="
105
+ calendarValue ? 'text-gray-900 dark:text-white' : 'text-gray-400'
106
+ "
107
+ >
108
+ {{ displayLabel }}
109
+ </span>
110
+ </UButton>
111
+
112
+ <template #content>
113
+ <div class="p-3 space-y-3">
114
+ <UCalendar
115
+ :model-value="calendarValue"
116
+ @update:model-value="onDateSelect"
117
+ />
118
+
119
+ <!-- Time picker -->
120
+ <div
121
+ class="flex items-center gap-2 border-t border-gray-200 dark:border-gray-700 pt-3"
122
+ >
123
+ <UIcon name="i-lucide-clock" class="text-gray-400 shrink-0" />
124
+ <div class="flex items-center gap-1">
125
+ <UInput
126
+ type="number"
127
+ :model-value="timeHour"
128
+ min="0"
129
+ max="23"
130
+ class="w-14 text-center"
131
+ @update:model-value="
132
+ (v) => onTimeChange('hour', String(v))
133
+ "
134
+ />
135
+ <span class="text-gray-500 font-medium">:</span>
136
+ <UInput
137
+ type="number"
138
+ :model-value="timeMinute"
139
+ min="0"
140
+ max="59"
141
+ class="w-14 text-center"
142
+ @update:model-value="
143
+ (v) => onTimeChange('minute', String(v))
144
+ "
145
+ />
146
+ </div>
147
+ <UButton
148
+ variant="ghost"
149
+ color="neutral"
150
+ size="xs"
151
+ icon="i-lucide-x"
152
+ class="ml-auto"
153
+ @click="clear"
154
+ />
155
+ </div>
156
+ </div>
157
+ </template>
158
+ </UPopover>
159
+ </template>
@@ -0,0 +1,17 @@
1
+ import type { DateDescriptor } from "../../types/index.js";
2
+ /** Datetime field with configurable naive/UTC serialisation semantics. */
3
+ type __VLS_Props = {
4
+ modelValue: unknown;
5
+ descriptor: DateDescriptor;
6
+ errors?: string[];
7
+ readonly?: boolean;
8
+ disabled?: boolean;
9
+ };
10
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
11
+ "update:modelValue": (value: string | null) => any;
12
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
13
+ "onUpdate:modelValue"?: ((value: string | null) => any) | undefined;
14
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
15
+ declare const _default: typeof __VLS_export;
16
+ export default _default;
17
+ //# sourceMappingURL=MapoDateTimeField.vue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MapoDateTimeField.vue.d.ts","sourceRoot":"","sources":["../../../../src/runtime/components/fields/MapoDateTimeField.vue"],"names":[],"mappings":"AA6MA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D,0EAA0E;AAC1E,KAAK,WAAW,GAAG;IACjB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,cAAc,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAgWF,QAAA,MAAM,YAAY;;;;kFAGhB,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}
@@ -0,0 +1,130 @@
1
+ <script setup>
2
+ import { ref, computed } from "vue";
3
+ import { useI18n } from "vue-i18n";
4
+ import { useMediaManager } from "../../composables/useMediaManager.js";
5
+ const props = defineProps({
6
+ modelValue: { type: null, required: true },
7
+ descriptor: { type: Object, required: true },
8
+ errors: { type: Array, required: false },
9
+ readonly: { type: Boolean, required: false },
10
+ disabled: { type: Boolean, required: false }
11
+ });
12
+ const emit = defineEmits(["update:modelValue"]);
13
+ const { t } = useI18n();
14
+ const dialogOpen = ref(false);
15
+ const { MediaDialog, available } = useMediaManager();
16
+ const current = computed(
17
+ () => props.modelValue?.media ?? null
18
+ );
19
+ const altValue = computed({
20
+ get: () => props.modelValue?.alt ?? "",
21
+ set: (v) => emit("update:modelValue", {
22
+ ...props.modelValue ?? { media: null },
23
+ alt: v
24
+ })
25
+ });
26
+ const captionValue = computed({
27
+ get: () => props.modelValue?.caption ?? "",
28
+ set: (v) => emit("update:modelValue", {
29
+ ...props.modelValue ?? { media: null },
30
+ caption: v
31
+ })
32
+ });
33
+ const mime = computed(() => props.descriptor.attrs?.mime);
34
+ function onConfirm(selection) {
35
+ const media = Array.isArray(selection) ? selection[0] ?? null : selection;
36
+ emit("update:modelValue", {
37
+ ...props.modelValue ?? {},
38
+ media,
39
+ alt: props.modelValue?.alt ?? media?.alt_text ?? "",
40
+ caption: props.modelValue?.caption ?? ""
41
+ });
42
+ }
43
+ function clear() {
44
+ emit("update:modelValue", null);
45
+ }
46
+ </script>
47
+
48
+ <template>
49
+ <div class="mapo-enhanced-media-field space-y-3">
50
+ <MapoMediaUnavailable v-if="!available" />
51
+
52
+ <template v-else>
53
+ <div class="flex gap-3">
54
+ <!-- Preview -->
55
+ <div class="shrink-0">
56
+ <div
57
+ class="flex size-24 cursor-pointer items-center justify-center overflow-hidden rounded-lg border-2 border-dashed transition-colors"
58
+ :class="
59
+ current ? 'border-default' : 'border-accented hover:border-primary'
60
+ "
61
+ @click="!readonly && !disabled && (dialogOpen = true)"
62
+ >
63
+ <MapoMediaPreview v-if="current" :media="current" size="md" />
64
+ <div v-else class="flex flex-col items-center gap-1 text-dimmed">
65
+ <UIcon name="i-lucide-image-plus" class="size-7" />
66
+ <span class="text-[10px]">{{ t("mapo.select") }}</span>
67
+ </div>
68
+ </div>
69
+ </div>
70
+
71
+ <!-- Metadata fields -->
72
+ <div class="flex-1 space-y-2">
73
+ <div v-if="current" class="flex items-start justify-between gap-2">
74
+ <p class="truncate text-xs text-muted">
75
+ {{ current.title || current.file?.split("/").pop() }}
76
+ </p>
77
+ <div class="flex gap-1">
78
+ <UButton
79
+ v-if="!readonly && !disabled"
80
+ icon="i-lucide-refresh-cw"
81
+ size="xs"
82
+ variant="ghost"
83
+ color="neutral"
84
+ :title="t('mapo.change')"
85
+ @click="dialogOpen = true"
86
+ />
87
+ <UButton
88
+ v-if="!readonly && !disabled"
89
+ icon="i-lucide-x"
90
+ size="xs"
91
+ variant="ghost"
92
+ color="neutral"
93
+ :title="t('mapo.remove')"
94
+ @click="clear"
95
+ />
96
+ </div>
97
+ </div>
98
+
99
+ <UInput
100
+ v-model="altValue"
101
+ size="sm"
102
+ :placeholder="t('mapo.enhancedMediaField.altText')"
103
+ :disabled="readonly || disabled || !current"
104
+ />
105
+ <UInput
106
+ v-model="captionValue"
107
+ size="sm"
108
+ :placeholder="t('mapo.enhancedMediaField.caption')"
109
+ :disabled="readonly || disabled || !current"
110
+ />
111
+ </div>
112
+ </div>
113
+
114
+ <!-- Dialog -->
115
+ <component
116
+ :is="MediaDialog"
117
+ v-model="dialogOpen"
118
+ selection-mode="single"
119
+ :mime="mime"
120
+ :selected="current"
121
+ @confirm="onConfirm"
122
+ />
123
+ </template>
124
+
125
+ <!-- Errors -->
126
+ <p v-for="err in errors" :key="err" class="text-xs text-error" role="alert">
127
+ {{ err }}
128
+ </p>
129
+ </div>
130
+ </template>
@@ -0,0 +1,16 @@
1
+ import type { EnhancedMediaDescriptor, EnhancedMediaValue } from "../../types/index.js";
2
+ type __VLS_Props = {
3
+ modelValue: EnhancedMediaValue | null | undefined;
4
+ descriptor: EnhancedMediaDescriptor;
5
+ errors?: string[];
6
+ readonly?: boolean;
7
+ disabled?: boolean;
8
+ };
9
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
10
+ "update:modelValue": (value: EnhancedMediaValue | null) => any;
11
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
12
+ "onUpdate:modelValue"?: ((value: EnhancedMediaValue | null) => any) | undefined;
13
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
14
+ declare const _default: typeof __VLS_export;
15
+ export default _default;
16
+ //# sourceMappingURL=MapoEnhancedMediaField.vue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MapoEnhancedMediaField.vue.d.ts","sourceRoot":"","sources":["../../../../src/runtime/components/fields/MapoEnhancedMediaField.vue"],"names":[],"mappings":"AA8JA,OAAO,KAAK,EACV,uBAAuB,EACvB,kBAAkB,EAEnB,MAAM,sBAAsB,CAAC;AAG9B,KAAK,WAAW,GAAG;IACjB,UAAU,EAAE,kBAAkB,GAAG,IAAI,GAAG,SAAS,CAAC;IAClD,UAAU,EAAE,uBAAuB,CAAC;IACpC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AA+UF,QAAA,MAAM,YAAY;;;;kFAGhB,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}
@@ -0,0 +1,99 @@
1
+ <script setup>
2
+ import { ref, computed } from "vue";
3
+ import { useI18n } from "vue-i18n";
4
+ const props = defineProps({
5
+ modelValue: { type: null, required: true },
6
+ descriptor: { type: Object, required: true },
7
+ errors: { type: Array, required: false },
8
+ readonly: { type: Boolean, required: false },
9
+ disabled: { type: Boolean, required: false }
10
+ });
11
+ const emit = defineEmits(["update:modelValue"]);
12
+ const { t } = useI18n();
13
+ const inputRef = ref(null);
14
+ const currentFileName = computed(() => {
15
+ if (props.modelValue instanceof File) return props.modelValue.name;
16
+ if (typeof props.modelValue === "string" && props.modelValue)
17
+ return props.modelValue.split("/").pop() ?? props.modelValue;
18
+ return null;
19
+ });
20
+ const previewUrl = computed(() => {
21
+ if (props.modelValue instanceof File) {
22
+ if (props.modelValue.type.startsWith("image/"))
23
+ return URL.createObjectURL(props.modelValue);
24
+ }
25
+ if (typeof props.modelValue === "string" && props.modelValue) {
26
+ const ext = props.modelValue.split(".").pop()?.toLowerCase() ?? "";
27
+ if (["jpg", "jpeg", "png", "gif", "webp", "svg", "avif"].includes(ext))
28
+ return props.modelValue;
29
+ }
30
+ return null;
31
+ });
32
+ function onFileChange(e) {
33
+ const file = e.target.files?.[0] ?? null;
34
+ emit("update:modelValue", file);
35
+ }
36
+ function clear() {
37
+ emit("update:modelValue", null);
38
+ if (inputRef.value) inputRef.value.value = "";
39
+ }
40
+ const hasError = computed(() => (props.errors?.length ?? 0) > 0);
41
+ const accept = computed(
42
+ () => props.descriptor.attrs?.accept ?? void 0
43
+ );
44
+ </script>
45
+
46
+ <template>
47
+ <div class="mapo-file-field space-y-2">
48
+ <!-- Current file preview -->
49
+ <div
50
+ v-if="currentFileName"
51
+ class="flex items-center gap-2 rounded border px-3 py-2 text-sm"
52
+ :class="
53
+ hasError ? 'border-red-300 bg-red-50' : 'border-gray-200 bg-gray-50'
54
+ "
55
+ >
56
+ <img
57
+ v-if="previewUrl"
58
+ :src="previewUrl"
59
+ class="h-8 w-8 rounded object-cover shrink-0"
60
+ alt=""
61
+ />
62
+ <UIcon
63
+ v-else
64
+ name="i-lucide-file"
65
+ class="size-4 shrink-0 text-gray-400"
66
+ />
67
+ <span class="flex-1 truncate text-gray-700">{{ currentFileName }}</span>
68
+ <UButton
69
+ v-if="!readonly && !disabled"
70
+ size="xs"
71
+ variant="ghost"
72
+ color="neutral"
73
+ icon="i-lucide-x"
74
+ :aria-label="t('mapo.fileField.removeFile')"
75
+ @click="clear"
76
+ />
77
+ </div>
78
+
79
+ <!-- File input -->
80
+ <UInput
81
+ v-if="!readonly && !disabled"
82
+ ref="inputRef"
83
+ type="file"
84
+ :accept="accept"
85
+ :color="hasError ? 'error' : void 0"
86
+ :aria-invalid="hasError ? 'true' : void 0"
87
+ @change="onFileChange"
88
+ />
89
+
90
+ <p
91
+ v-for="err in errors"
92
+ :key="err"
93
+ class="text-sm text-red-500"
94
+ role="alert"
95
+ >
96
+ {{ err }}
97
+ </p>
98
+ </div>
99
+ </template>
@@ -0,0 +1,16 @@
1
+ import type { FileDescriptor } from "../../types/index.js";
2
+ type __VLS_Props = {
3
+ modelValue: unknown;
4
+ descriptor: FileDescriptor;
5
+ errors?: string[];
6
+ readonly?: boolean;
7
+ disabled?: boolean;
8
+ };
9
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
10
+ "update:modelValue": (value: File | null) => any;
11
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
12
+ "onUpdate:modelValue"?: ((value: File | null) => any) | undefined;
13
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
14
+ declare const _default: typeof __VLS_export;
15
+ export default _default;
16
+ //# sourceMappingURL=MapoFileField.vue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MapoFileField.vue.d.ts","sourceRoot":"","sources":["../../../../src/runtime/components/fields/MapoFileField.vue"],"names":[],"mappings":"AAiHA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D,KAAK,WAAW,GAAG;IACjB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,cAAc,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AA6MF,QAAA,MAAM,YAAY;;;;kFAGhB,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}