@mapomodule/form 2.0.0-beta.4 → 2.0.0-beta.6

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 (83) hide show
  1. package/README.md +2 -1
  2. package/dist/index.d.mts +1 -1
  3. package/dist/index.d.ts +1 -1
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/module.d.ts.map +1 -1
  6. package/dist/module.json +1 -1
  7. package/dist/module.mjs +16 -1
  8. package/dist/runtime/components/MapoFocusPortal.vue +3 -1
  9. package/dist/runtime/components/MapoFocusPortal.vue.d.ts.map +1 -1
  10. package/dist/runtime/components/MapoFormTabs.vue +4 -2
  11. package/dist/runtime/components/MapoFormTabs.vue.d.ts.map +1 -1
  12. package/dist/runtime/components/fields/MapoDateField.vue +3 -1
  13. package/dist/runtime/components/fields/MapoDateField.vue.d.ts.map +1 -1
  14. package/dist/runtime/components/fields/MapoDateTimeField.vue +5 -3
  15. package/dist/runtime/components/fields/MapoDateTimeField.vue.d.ts.map +1 -1
  16. package/dist/runtime/components/fields/MapoEnhancedMediaField.vue +130 -0
  17. package/dist/runtime/components/fields/MapoEnhancedMediaField.vue.d.ts +16 -0
  18. package/dist/runtime/components/fields/MapoEnhancedMediaField.vue.d.ts.map +1 -0
  19. package/dist/runtime/components/fields/MapoFileField.vue +3 -1
  20. package/dist/runtime/components/fields/MapoFileField.vue.d.ts.map +1 -1
  21. package/dist/runtime/components/fields/MapoFksField.vue +6 -2
  22. package/dist/runtime/components/fields/MapoFksField.vue.d.ts.map +1 -1
  23. package/dist/runtime/components/fields/MapoMapField.vue +4 -2
  24. package/dist/runtime/components/fields/MapoMapField.vue.d.ts.map +1 -1
  25. package/dist/runtime/components/fields/MapoMediaField.vue +91 -0
  26. package/dist/runtime/components/fields/MapoMediaField.vue.d.ts +16 -0
  27. package/dist/runtime/components/fields/MapoMediaField.vue.d.ts.map +1 -0
  28. package/dist/runtime/components/fields/MapoMediaM2mField.vue +110 -0
  29. package/dist/runtime/components/fields/MapoMediaM2mField.vue.d.ts +16 -0
  30. package/dist/runtime/components/fields/MapoMediaM2mField.vue.d.ts.map +1 -0
  31. package/dist/runtime/components/fields/MapoMediaUnavailable.vue +15 -0
  32. package/dist/runtime/components/fields/MapoMediaUnavailable.vue.d.ts +4 -0
  33. package/dist/runtime/components/fields/MapoMediaUnavailable.vue.d.ts.map +1 -0
  34. package/dist/runtime/components/fields/MapoRepeater.vue +32 -22
  35. package/dist/runtime/components/fields/MapoRepeater.vue.d.ts.map +1 -1
  36. package/dist/runtime/components/fields/MapoRepeaterItem.vue +13 -8
  37. package/dist/runtime/components/fields/MapoRepeaterItem.vue.d.ts.map +1 -1
  38. package/dist/runtime/components/fields/MapoSeoPreview.vue +16 -12
  39. package/dist/runtime/components/fields/MapoSeoPreview.vue.d.ts.map +1 -1
  40. package/dist/runtime/components/fields/MapoWygEditor.vue +30 -5
  41. package/dist/runtime/components/fields/MapoWygEditor.vue.d.ts.map +1 -1
  42. package/dist/runtime/composables/useMapoForm.d.ts.map +1 -1
  43. package/dist/runtime/composables/useMapoForm.js +4 -2
  44. package/dist/runtime/composables/useMediaManager.d.ts +17 -0
  45. package/dist/runtime/composables/useMediaManager.d.ts.map +1 -0
  46. package/dist/runtime/composables/useMediaManager.js +6 -0
  47. package/dist/runtime/composables/useUnsavedChangesGuard.d.ts.map +1 -1
  48. package/dist/runtime/composables/useUnsavedChangesGuard.js +3 -2
  49. package/dist/runtime/registry/defaults.d.ts.map +1 -1
  50. package/dist/runtime/registry/defaults.js +8 -2
  51. package/dist/runtime/types/descriptor.d.ts +18 -4
  52. package/dist/runtime/types/descriptor.d.ts.map +1 -1
  53. package/dist/runtime/types/index.d.ts +2 -1
  54. package/dist/runtime/types/index.d.ts.map +1 -1
  55. package/dist/runtime/types/media.d.ts +43 -0
  56. package/dist/runtime/types/media.d.ts.map +1 -0
  57. package/dist/runtime/types/media.js +0 -0
  58. package/package.json +9 -6
  59. package/src/__tests__/useMapoForm.test.ts +5 -0
  60. package/src/index.ts +5 -0
  61. package/src/module.ts +24 -0
  62. package/src/runtime/components/MapoFocusPortal.vue +3 -1
  63. package/src/runtime/components/MapoFormTabs.vue +4 -1
  64. package/src/runtime/components/fields/MapoDateField.vue +3 -1
  65. package/src/runtime/components/fields/MapoDateTimeField.vue +3 -1
  66. package/src/runtime/components/fields/MapoEnhancedMediaField.vue +153 -0
  67. package/src/runtime/components/fields/MapoFileField.vue +3 -1
  68. package/src/runtime/components/fields/MapoFksField.vue +7 -2
  69. package/src/runtime/components/fields/MapoMapField.vue +5 -2
  70. package/src/runtime/components/fields/MapoMediaField.vue +104 -0
  71. package/src/runtime/components/fields/MapoMediaM2mField.vue +130 -0
  72. package/src/runtime/components/fields/MapoMediaUnavailable.vue +19 -0
  73. package/src/runtime/components/fields/MapoRepeater.vue +33 -26
  74. package/src/runtime/components/fields/MapoRepeaterItem.vue +14 -8
  75. package/src/runtime/components/fields/MapoSeoPreview.vue +17 -13
  76. package/src/runtime/components/fields/MapoWygEditor.vue +47 -5
  77. package/src/runtime/composables/useMapoForm.ts +4 -2
  78. package/src/runtime/composables/useMediaManager.ts +21 -0
  79. package/src/runtime/composables/useUnsavedChangesGuard.ts +3 -3
  80. package/src/runtime/registry/defaults.ts +9 -1
  81. package/src/runtime/types/descriptor.ts +22 -7
  82. package/src/runtime/types/index.ts +4 -0
  83. package/src/runtime/types/media.ts +44 -0
package/README.md CHANGED
@@ -5,7 +5,7 @@ Form engine for Mapo v2 — descriptor-driven, typesafe, headless-ready admin fo
5
5
  ## Features
6
6
 
7
7
  - **Descriptor-driven**: define fields as `FieldDescriptor[]` — layout, validation, i18n in one object
8
- - **All field types built-in**: text, select, date/time, FK/M2M autocomplete, WYSIWYG editor, SEO preview, map, repeater
8
+ - **All field types built-in**: text, select, date/time, FK/M2M autocomplete, WYSIWYG editor, SEO preview, map, repeater, media pickers
9
9
  - **Headless composable**: `useMapoForm()` for fully custom layouts; `<MapoForm>` for the default shell
10
10
  - **Registry auto-injection**: `<MapoForm>` and `<MapoDetail>` resolve `$mapoFormRegistry` automatically — no boilerplate
11
11
  - **Typed extension API**: `defineFormField(type, entry, opts)` to register custom fields with collision detection
@@ -16,6 +16,7 @@ Form engine for Mapo v2 — descriptor-driven, typesafe, headless-ready admin fo
16
16
  - **Consistent repeater items**: `MapoRepeater` drives each item through `useMapoForm`, so nested-path keys, `translatable`, accessors, `descriptor.is` overrides and **client validation** behave identically inside a repeater
17
17
  - **Draft persistence**: `draftKey` option on `useMapoForm()` auto-saves to localStorage every 2 s; `checkDraft()` / `draftBanner` offer restore/discard; clears on successful `submit()`
18
18
  - **Fail-soft**: unknown field types render a non-destructive placeholder, never crash the form
19
+ - **Media pickers**: `media`, `media-m2m` and `enhanced-media` field types are built in; they drive the Media Manager from [`@mapomodule/uikit`](../uikit/README.md), resolved at runtime — install it to enable them
19
20
 
20
21
  ## Install
21
22
 
package/dist/index.d.mts CHANGED
@@ -5,5 +5,5 @@ export { FocusTarget, useFocusMode } from '../dist/runtime/composables/useFocusM
5
5
  export { UnsavedChangesGuardOptions, useUnsavedChangesGuard } from '../dist/runtime/composables/useUnsavedChangesGuard.js';
6
6
  export { greaterThan, isEmpty, isNoneOf, isNotEmpty, isOneOf, lessThan, matches, matchesField, when, whenAny, whenNot } from '../dist/runtime/composables/useProgressiveDisclosure.js';
7
7
  export { FieldGroupDescriptor, flattenFieldGroups } from '../dist/runtime/utils/flattenFieldGroups.js';
8
- export { AnyFieldDescriptor, BooleanDescriptor, ColorDescriptor, CustomDescriptor, DateDescriptor, EditorDescriptor, FieldAccessor, FieldComponentEntry, FieldDescriptor, FieldRegistry, FieldType, FileDescriptor, FksDescriptor, KnownFieldType, MapDescriptor, MediaDescriptor, NumberDescriptor, PartialFieldRegistry, RepeaterDescriptor, SelectDescriptor, SeoDescriptor, TextDescriptor } from '../dist/runtime/types/index.js';
8
+ export { AnyFieldDescriptor, BooleanDescriptor, ColorDescriptor, CustomDescriptor, DateDescriptor, EditorDescriptor, EnhancedMediaDescriptor, EnhancedMediaValue, FieldAccessor, FieldComponentEntry, FieldDescriptor, FieldRegistry, FieldType, FileDescriptor, FksDescriptor, KnownFieldType, MapDescriptor, MediaDescriptor, MediaItem, MediaLink, MediaM2mDescriptor, NumberDescriptor, PartialFieldRegistry, RepeaterDescriptor, SelectDescriptor, SeoDescriptor, TextDescriptor } from '../dist/runtime/types/index.js';
9
9
  import '@nuxt/schema';
package/dist/index.d.ts CHANGED
@@ -11,5 +11,5 @@ export { when, whenAny, whenNot, matchesField, isOneOf, isNoneOf, isNotEmpty, is
11
11
  export { flattenFieldGroups } from "./runtime/utils/flattenFieldGroups.js";
12
12
  export type { FieldGroupDescriptor } from "./runtime/utils/flattenFieldGroups.js";
13
13
  export { KnownFieldType } from "./runtime/types/index.js";
14
- export type { FieldType, FieldAccessor, FieldDescriptor, AnyFieldDescriptor, TextDescriptor, NumberDescriptor, BooleanDescriptor, SelectDescriptor, FksDescriptor, DateDescriptor, ColorDescriptor, FileDescriptor, EditorDescriptor, SeoDescriptor, MapDescriptor, RepeaterDescriptor, MediaDescriptor, CustomDescriptor, FieldComponentEntry, FieldRegistry, PartialFieldRegistry, } from "./runtime/types/index.js";
14
+ export type { FieldType, FieldAccessor, FieldDescriptor, AnyFieldDescriptor, TextDescriptor, NumberDescriptor, BooleanDescriptor, SelectDescriptor, FksDescriptor, DateDescriptor, ColorDescriptor, FileDescriptor, EditorDescriptor, SeoDescriptor, MapDescriptor, RepeaterDescriptor, MediaDescriptor, MediaM2mDescriptor, EnhancedMediaDescriptor, CustomDescriptor, MediaLink, MediaItem, EnhancedMediaValue, FieldComponentEntry, FieldRegistry, PartialFieldRegistry, } from "./runtime/types/index.js";
15
15
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,4CAA4C,CAAC;AAC/E,YAAY,EAAE,wBAAwB,EAAE,MAAM,4CAA4C,CAAC;AAC3F,OAAO,EAAE,YAAY,EAAE,MAAM,uCAAuC,CAAC;AACrE,YAAY,EAAE,mBAAmB,EAAE,MAAM,uCAAuC,CAAC;AACjF,OAAO,EAAE,YAAY,EAAE,MAAM,uCAAuC,CAAC;AACrE,YAAY,EAAE,WAAW,EAAE,MAAM,uCAAuC,CAAC;AACzE,OAAO,EAAE,sBAAsB,EAAE,MAAM,iDAAiD,CAAC;AACzF,YAAY,EAAE,0BAA0B,EAAE,MAAM,iDAAiD,CAAC;AAElG,OAAO,EACL,IAAI,EACJ,OAAO,EACP,OAAO,EACP,YAAY,EACZ,OAAO,EACP,QAAQ,EACR,UAAU,EACV,OAAO,EACP,WAAW,EACX,QAAQ,EACR,OAAO,GACR,MAAM,mDAAmD,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAC3E,YAAY,EAAE,oBAAoB,EAAE,MAAM,uCAAuC,CAAC;AAClF,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC1D,YAAY,EACV,SAAS,EACT,aAAa,EACb,eAAe,EACf,kBAAkB,EAClB,cAAc,EACd,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,EAChB,aAAa,EACb,cAAc,EACd,eAAe,EACf,cAAc,EACd,gBAAgB,EAChB,aAAa,EACb,aAAa,EACb,kBAAkB,EAClB,eAAe,EACf,gBAAgB,EAChB,mBAAmB,EACnB,aAAa,EACb,oBAAoB,GACrB,MAAM,0BAA0B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,4CAA4C,CAAC;AAC/E,YAAY,EAAE,wBAAwB,EAAE,MAAM,4CAA4C,CAAC;AAC3F,OAAO,EAAE,YAAY,EAAE,MAAM,uCAAuC,CAAC;AACrE,YAAY,EAAE,mBAAmB,EAAE,MAAM,uCAAuC,CAAC;AACjF,OAAO,EAAE,YAAY,EAAE,MAAM,uCAAuC,CAAC;AACrE,YAAY,EAAE,WAAW,EAAE,MAAM,uCAAuC,CAAC;AACzE,OAAO,EAAE,sBAAsB,EAAE,MAAM,iDAAiD,CAAC;AACzF,YAAY,EAAE,0BAA0B,EAAE,MAAM,iDAAiD,CAAC;AAElG,OAAO,EACL,IAAI,EACJ,OAAO,EACP,OAAO,EACP,YAAY,EACZ,OAAO,EACP,QAAQ,EACR,UAAU,EACV,OAAO,EACP,WAAW,EACX,QAAQ,EACR,OAAO,GACR,MAAM,mDAAmD,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAC3E,YAAY,EAAE,oBAAoB,EAAE,MAAM,uCAAuC,CAAC;AAClF,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC1D,YAAY,EACV,SAAS,EACT,aAAa,EACb,eAAe,EACf,kBAAkB,EAClB,cAAc,EACd,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,EAChB,aAAa,EACb,cAAc,EACd,eAAe,EACf,cAAc,EACd,gBAAgB,EAChB,aAAa,EACb,aAAa,EACb,kBAAkB,EAClB,eAAe,EACf,kBAAkB,EAClB,uBAAuB,EACvB,gBAAgB,EAChB,SAAS,EACT,SAAS,EACT,kBAAkB,EAClB,mBAAmB,EACnB,aAAa,EACb,oBAAoB,GACrB,MAAM,0BAA0B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAErE,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,EAAE,oBAAoB,CAAC;IAC9B,MAAM,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;;AAED,wBA4MyC"}
1
+ {"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAErE,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,EAAE,oBAAoB,CAAC;IAC9B,MAAM,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;;AAED,wBAkOyC"}
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mapomodule/form",
3
3
  "configKey": "mapoForm",
4
- "version": "2.0.0-beta.4",
4
+ "version": "2.0.0-beta.6",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "unknown"
package/dist/module.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { defineNuxtModule, createResolver, addPlugin, addTypeTemplate, extendPages, addComponent, addImports } from '@nuxt/kit';
1
+ import { defineNuxtModule, createResolver, hasNuxtModule, installModule, addPlugin, addTypeTemplate, extendPages, addComponent, addImports } from '@nuxt/kit';
2
2
 
3
3
  const module$1 = defineNuxtModule({
4
4
  meta: {
@@ -16,6 +16,9 @@ const module$1 = defineNuxtModule({
16
16
  (nuxt.options.imports.transform.include ??= []).push(
17
17
  /@mapomodule[\\/]form[\\/]/
18
18
  );
19
+ if (!hasNuxtModule("@mapomodule/i18n")) {
20
+ await installModule(await resolver.resolvePath("@mapomodule/i18n"));
21
+ }
19
22
  nuxt.options.runtimeConfig.public.mapoForm = {
20
23
  groups: { expanded: options.groups?.expanded ?? true },
21
24
  debounce: options.debounce ?? 300,
@@ -74,6 +77,18 @@ export {};
74
77
  filePath: resolver.resolve(`./runtime/components/${name}.vue`)
75
78
  });
76
79
  }
80
+ const mediaFieldComponents = [
81
+ "MapoMediaField",
82
+ "MapoMediaM2mField",
83
+ "MapoEnhancedMediaField",
84
+ "MapoMediaUnavailable"
85
+ ];
86
+ for (const name of mediaFieldComponents) {
87
+ addComponent({
88
+ name,
89
+ filePath: resolver.resolve(`./runtime/components/fields/${name}.vue`)
90
+ });
91
+ }
77
92
  addImports([
78
93
  {
79
94
  name: "useMapoForm",
@@ -1,8 +1,10 @@
1
1
  <script setup>
2
2
  import { computed, ref, onMounted, onUnmounted } from "vue";
3
3
  import { watch } from "vue";
4
+ import { useI18n } from "vue-i18n";
4
5
  import { useFocusMode } from "../composables/useFocusMode.js";
5
6
  import MapoForm from "./MapoForm.vue";
7
+ const { t } = useI18n();
6
8
  const { focusTarget, exit } = useFocusMode();
7
9
  const isOpen = computed(() => focusTarget.value !== null);
8
10
  const canUseDocument = typeof document !== "undefined";
@@ -66,7 +68,7 @@ function onUpdate(val) {
66
68
  variant="ghost"
67
69
  color="neutral"
68
70
  icon="i-lucide-x"
69
- label="Close"
71
+ :label="t('mapo.close')"
70
72
  size="sm"
71
73
  @click="exit"
72
74
  />
@@ -1 +1 @@
1
- {"version":3,"file":"MapoFocusPortal.vue.d.ts","sourceRoot":"","sources":["../../../src/runtime/components/MapoFocusPortal.vue"],"names":[],"mappings":"AAkdA,QAAA,MAAM,YAAY,+QAChB,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}
1
+ {"version":3,"file":"MapoFocusPortal.vue.d.ts","sourceRoot":"","sources":["../../../src/runtime/components/MapoFocusPortal.vue"],"names":[],"mappings":"AAudA,QAAA,MAAM,YAAY,+QAChB,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}
@@ -1,8 +1,10 @@
1
1
  <script setup>
2
2
  import { ref, computed } from "vue";
3
+ import { useI18n } from "vue-i18n";
3
4
  import { injectMapoForm } from "../composables/useMapoForm.js";
4
5
  import MapoFormGroup from "./MapoFormGroup.vue";
5
6
  import MapoFormFlatSection from "./MapoFormFlatSection.vue";
7
+ const { t } = useI18n();
6
8
  const props = defineProps({
7
9
  tabs: { type: Array, required: true }
8
10
  });
@@ -21,7 +23,7 @@ const form = injectMapoForm();
21
23
  const tabsWithErrors = computed(() => {
22
24
  const errorKeys = new Set(Object.keys(form.errors.value));
23
25
  return new Set(
24
- props.tabs.filter((tab) => tabHasErrors(tab, errorKeys)).map((t) => t.name)
26
+ props.tabs.filter((tab) => tabHasErrors(tab, errorKeys)).map((t2) => t2.name)
25
27
  );
26
28
  });
27
29
  </script>
@@ -46,7 +48,7 @@ const tabsWithErrors = computed(() => {
46
48
  <span
47
49
  v-if="tabsWithErrors.has(tab.name)"
48
50
  class="inline-block h-1.5 w-1.5 rounded-full bg-red-500"
49
- aria-label="This tab contains errors"
51
+ :aria-label="t('mapo.form.tabErrors')"
50
52
  />
51
53
  </button>
52
54
  <!-- Extra content appended to the tab bar (actions, badges, …). -->
@@ -1 +1 @@
1
- {"version":3,"file":"MapoFormTabs.vue.d.ts","sourceRoot":"","sources":["../../../src/runtime/components/MapoFormTabs.vue"],"names":[],"mappings":"AAwHA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAKnD,KAAK,WAAW,GAAG;IAAE,IAAI,EAAE,QAAQ,EAAE,CAAA;CAAE,CAAC;AAGxC,KAAK,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAqO3C,QAAA,MAAM,UAAU,kSAEd,CAAC;AACH,QAAA,MAAM,YAAY,EAAS,eAAe,CAAC,OAAO,UAAU,EAAE,WAAW,CAAC,CAAC;wBACtD,OAAO,YAAY;AAAxC,wBAAyC;AACzC,KAAK,eAAe,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IAChC,QAAO;QACN,MAAM,EAAE,CAAC,CAAC;KACV,CAAA;CACD,CAAC"}
1
+ {"version":3,"file":"MapoFormTabs.vue.d.ts","sourceRoot":"","sources":["../../../src/runtime/components/MapoFormTabs.vue"],"names":[],"mappings":"AA4HA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAOnD,KAAK,WAAW,GAAG;IAAE,IAAI,EAAE,QAAQ,EAAE,CAAA;CAAE,CAAC;AAGxC,KAAK,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAsO3C,QAAA,MAAM,UAAU,kSAEd,CAAC;AACH,QAAA,MAAM,YAAY,EAAS,eAAe,CAAC,OAAO,UAAU,EAAE,WAAW,CAAC,CAAC;wBACtD,OAAO,YAAY;AAAxC,wBAAyC;AACzC,KAAK,eAAe,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IAChC,QAAO;QACN,MAAM,EAAE,CAAC,CAAC;KACV,CAAA;CACD,CAAC"}
@@ -1,5 +1,6 @@
1
1
  <script setup>
2
2
  import { computed, ref } from "vue";
3
+ import { useI18n } from "vue-i18n";
3
4
  import { parseDate, CalendarDate } from "@internationalized/date";
4
5
  const props = defineProps({
5
6
  modelValue: { type: null, required: true },
@@ -9,6 +10,7 @@ const props = defineProps({
9
10
  disabled: { type: Boolean, required: false }
10
11
  });
11
12
  const emit = defineEmits(["update:modelValue"]);
13
+ const { t } = useI18n();
12
14
  const open = ref(false);
13
15
  const calendarValue = computed(() => {
14
16
  const v = props.modelValue;
@@ -21,7 +23,7 @@ const calendarValue = computed(() => {
21
23
  });
22
24
  const displayLabel = computed(() => {
23
25
  const v = calendarValue.value;
24
- if (!v) return "Select date";
26
+ if (!v) return t("mapo.dateField.selectDate");
25
27
  return `${String(v.day).padStart(2, "0")}/${String(v.month).padStart(2, "0")}/${v.year}`;
26
28
  });
27
29
  const attrs = computed(() => props.descriptor.attrs ?? {});
@@ -1 +1 @@
1
- {"version":3,"file":"MapoDateField.vue.d.ts","sourceRoot":"","sources":["../../../../src/runtime/components/fields/MapoDateField.vue"],"names":[],"mappings":"AAuGA,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;AAwMF,QAAA,MAAM,YAAY;;;;kFAGhB,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}
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"}
@@ -6,6 +6,7 @@ import {
6
6
  today,
7
7
  getLocalTimeZone
8
8
  } from "@internationalized/date";
9
+ import { useI18n } from "vue-i18n";
9
10
  const props = defineProps({
10
11
  modelValue: { type: null, required: true },
11
12
  descriptor: { type: Object, required: true },
@@ -14,6 +15,7 @@ const props = defineProps({
14
15
  disabled: { type: Boolean, required: false }
15
16
  });
16
17
  const emit = defineEmits(["update:modelValue"]);
18
+ const { t } = useI18n();
17
19
  const open = ref(false);
18
20
  const tzMode = computed(() => props.descriptor.attrs?.tz ?? "naive");
19
21
  const calendarValue = computed(() => {
@@ -48,7 +50,7 @@ function emitFromCalendar(v) {
48
50
  }
49
51
  const displayLabel = computed(() => {
50
52
  const v = calendarValue.value;
51
- if (!v) return "Select date and time";
53
+ if (!v) return t("mapo.dateField.selectDateTime");
52
54
  const date = `${String(v.day).padStart(2, "0")}/${String(v.month).padStart(2, "0")}/${v.year}`;
53
55
  const time = `${String(v.hour).padStart(2, "0")}:${String(v.minute).padStart(2, "0")}`;
54
56
  return `${date} ${time}`;
@@ -72,8 +74,8 @@ function onTimeChange(part, val) {
72
74
  Math.min(part === "hour" ? 23 : 59, parseInt(val) || 0)
73
75
  );
74
76
  const base = calendarValue.value ?? (() => {
75
- const t = today(getLocalTimeZone());
76
- return new CalendarDateTime(t.year, t.month, t.day, 0, 0);
77
+ const t2 = today(getLocalTimeZone());
78
+ return new CalendarDateTime(t2.year, t2.month, t2.day, 0, 0);
77
79
  })();
78
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);
79
81
  emitFromCalendar(merged);
@@ -1 +1 @@
1
- {"version":3,"file":"MapoDateTimeField.vue.d.ts","sourceRoot":"","sources":["../../../../src/runtime/components/fields/MapoDateTimeField.vue"],"names":[],"mappings":"AA0MA,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;AA+VF,QAAA,MAAM,YAAY;;;;kFAGhB,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}
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"}
@@ -1,5 +1,6 @@
1
1
  <script setup>
2
2
  import { ref, computed } from "vue";
3
+ import { useI18n } from "vue-i18n";
3
4
  const props = defineProps({
4
5
  modelValue: { type: null, required: true },
5
6
  descriptor: { type: Object, required: true },
@@ -8,6 +9,7 @@ const props = defineProps({
8
9
  disabled: { type: Boolean, required: false }
9
10
  });
10
11
  const emit = defineEmits(["update:modelValue"]);
12
+ const { t } = useI18n();
11
13
  const inputRef = ref(null);
12
14
  const currentFileName = computed(() => {
13
15
  if (props.modelValue instanceof File) return props.modelValue.name;
@@ -69,7 +71,7 @@ const accept = computed(
69
71
  variant="ghost"
70
72
  color="neutral"
71
73
  icon="i-lucide-x"
72
- aria-label="Remove file"
74
+ :aria-label="t('mapo.fileField.removeFile')"
73
75
  @click="clear"
74
76
  />
75
77
  </div>
@@ -1 +1 @@
1
- {"version":3,"file":"MapoFileField.vue.d.ts","sourceRoot":"","sources":["../../../../src/runtime/components/fields/MapoFileField.vue"],"names":[],"mappings":"AA8GA,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;AA2MF,QAAA,MAAM,YAAY;;;;kFAGhB,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}
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"}
@@ -1,5 +1,6 @@
1
1
  <script setup>
2
2
  import { ref, computed, watch, onMounted } from "vue";
3
+ import { useI18n } from "vue-i18n";
3
4
  import { useNuxtApp } from "#app";
4
5
  import { debounce } from "@mapomodule/utils";
5
6
  const props = defineProps({
@@ -10,6 +11,7 @@ const props = defineProps({
10
11
  disabled: { type: Boolean, required: false }
11
12
  });
12
13
  const emit = defineEmits(["update:modelValue"]);
14
+ const { t } = useI18n();
13
15
  const attrs = computed(() => props.descriptor.attrs);
14
16
  const isMultiple = computed(
15
17
  () => props.descriptor.type === "m2m" || !!attrs.value.multiple
@@ -43,7 +45,7 @@ const internalValue = computed({
43
45
  set: (val) => emit("update:modelValue", val)
44
46
  });
45
47
  const placeholder = computed(
46
- () => isMultiple.value ? "Search and select..." : "Search..."
48
+ () => isMultiple.value ? t("mapo.fksField.searchSelect") : t("mapo.search")
47
49
  );
48
50
  function getLabel(item) {
49
51
  if (item !== null && item !== void 0 && typeof item !== "object") {
@@ -111,7 +113,9 @@ function getItemKey(item) {
111
113
  <!-- Empty state -->
112
114
  <template #empty>
113
115
  <span class="text-sm text-gray-500">
114
- {{ searchTerm ? "No results found" : "No items available" }}
116
+ {{
117
+ searchTerm ? t("mapo.fksField.noResults") : t("mapo.fksField.noItems")
118
+ }}
115
119
  </span>
116
120
  </template>
117
121
  </USelectMenu>
@@ -1 +1 @@
1
- {"version":3,"file":"MapoFksField.vue.d.ts","sourceRoot":"","sources":["../../../../src/runtime/components/fields/MapoFksField.vue"],"names":[],"mappings":"AA6JA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE1D,KAAK,WAAW,GAAG;IACjB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,aAAa,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AA4PF,QAAA,MAAM,YAAY;;;;kFAGhB,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}
1
+ {"version":3,"file":"MapoFksField.vue.d.ts","sourceRoot":"","sources":["../../../../src/runtime/components/fields/MapoFksField.vue"],"names":[],"mappings":"AAmKA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE1D,KAAK,WAAW,GAAG;IACjB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,aAAa,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAiQF,QAAA,MAAM,YAAY;;;;kFAGhB,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}
@@ -1,8 +1,10 @@
1
1
  <script setup>
2
2
  import { ref, computed, defineAsyncComponent } from "vue";
3
+ import { useI18n } from "vue-i18n";
3
4
  const LeafletMap = defineAsyncComponent(
4
5
  () => import("./MapoMapFieldClient.vue")
5
6
  );
7
+ const { t } = useI18n();
6
8
  const props = defineProps({
7
9
  modelValue: { type: null, required: true },
8
10
  descriptor: { type: Object, required: true },
@@ -43,7 +45,7 @@ function clear() {
43
45
  <div class="flex gap-2">
44
46
  <UInput
45
47
  v-model="latInput"
46
- placeholder="Latitude"
48
+ :placeholder="t('mapo.latitude')"
47
49
  class="flex-1"
48
50
  :readonly="readonly"
49
51
  :disabled="disabled"
@@ -51,7 +53,7 @@ function clear() {
51
53
  />
52
54
  <UInput
53
55
  v-model="lngInput"
54
- placeholder="Longitude"
56
+ :placeholder="t('mapo.longitude')"
55
57
  class="flex-1"
56
58
  :readonly="readonly"
57
59
  :disabled="disabled"
@@ -1 +1 @@
1
- {"version":3,"file":"MapoMapField.vue.d.ts","sourceRoot":"","sources":["../../../../src/runtime/components/fields/MapoMapField.vue"],"names":[],"mappings":"AAyHA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAO1D,sDAAsD;AACtD,UAAU,MAAM;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;CACb;AAED,8EAA8E;AAC9E,KAAK,WAAW,GAAG;IACjB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,aAAa,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAoOF,QAAA,MAAM,YAAY;;;;kFAGhB,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}
1
+ {"version":3,"file":"MapoMapField.vue.d.ts","sourceRoot":"","sources":["../../../../src/runtime/components/fields/MapoMapField.vue"],"names":[],"mappings":"AA6HA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAS1D,sDAAsD;AACtD,UAAU,MAAM;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;CACb;AAED,8EAA8E;AAC9E,KAAK,WAAW,GAAG;IACjB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,aAAa,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAqOF,QAAA,MAAM,YAAY;;;;kFAGhB,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}
@@ -0,0 +1,91 @@
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
+ if (!props.modelValue) return null;
18
+ if (typeof props.modelValue === "object") return props.modelValue;
19
+ return null;
20
+ });
21
+ const mime = computed(() => props.descriptor.attrs?.mime);
22
+ function onConfirm(selection) {
23
+ emit(
24
+ "update:modelValue",
25
+ Array.isArray(selection) ? selection[0] ?? null : selection
26
+ );
27
+ }
28
+ function clear() {
29
+ emit("update:modelValue", null);
30
+ }
31
+ </script>
32
+
33
+ <template>
34
+ <div class="mapo-media-field space-y-2">
35
+ <MapoMediaUnavailable v-if="!available" />
36
+
37
+ <template v-else>
38
+ <!-- Selected media preview -->
39
+ <div
40
+ v-if="current"
41
+ class="flex items-center gap-3 rounded-lg border border-default p-2"
42
+ >
43
+ <MapoMediaPreview :media="current" size="sm" />
44
+ <div class="flex-1 min-w-0">
45
+ <p class="truncate text-xs font-medium">
46
+ {{ current.title || current.file?.split("/").pop() }}
47
+ </p>
48
+ <p class="text-xs text-dimmed">
49
+ {{ current.mime_type }}
50
+ </p>
51
+ </div>
52
+ <UButton
53
+ v-if="!readonly && !disabled"
54
+ icon="i-lucide-x"
55
+ size="xs"
56
+ variant="ghost"
57
+ color="neutral"
58
+ @click="clear"
59
+ />
60
+ </div>
61
+
62
+ <!-- Trigger button -->
63
+ <UButton
64
+ v-if="!readonly && !disabled"
65
+ :icon="current ? 'i-lucide-refresh-cw' : 'i-lucide-image-plus'"
66
+ size="sm"
67
+ variant="soft"
68
+ color="neutral"
69
+ :block="!current"
70
+ @click="dialogOpen = true"
71
+ >
72
+ {{ current ? t("mapo.change") : t("mapo.mediaField.selectMedia") }}
73
+ </UButton>
74
+
75
+ <!-- Dialog -->
76
+ <component
77
+ :is="MediaDialog"
78
+ v-model="dialogOpen"
79
+ selection-mode="single"
80
+ :mime="mime"
81
+ :selected="current"
82
+ @confirm="onConfirm"
83
+ />
84
+ </template>
85
+
86
+ <!-- Errors -->
87
+ <p v-for="err in errors" :key="err" class="text-xs text-error" role="alert">
88
+ {{ err }}
89
+ </p>
90
+ </div>
91
+ </template>
@@ -0,0 +1,16 @@
1
+ import type { MediaDescriptor, MediaItem } from "../../types/index.js";
2
+ type __VLS_Props = {
3
+ modelValue: MediaItem | number | null | undefined;
4
+ descriptor: MediaDescriptor;
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: MediaItem | null) => any;
11
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
12
+ "onUpdate:modelValue"?: ((value: MediaItem | null) => any) | undefined;
13
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
14
+ declare const _default: typeof __VLS_export;
15
+ export default _default;
16
+ //# sourceMappingURL=MapoMediaField.vue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MapoMediaField.vue.d.ts","sourceRoot":"","sources":["../../../../src/runtime/components/fields/MapoMediaField.vue"],"names":[],"mappings":"AA6GA,OAAO,KAAK,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAGvE,KAAK,WAAW,GAAG;IACjB,UAAU,EAAE,SAAS,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAClD,UAAU,EAAE,eAAe,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAgOF,QAAA,MAAM,YAAY;;;;kFAGhB,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}