@shwfed/nuxt 0.11.35 → 0.11.37

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 (24) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/runtime/components/fields.d.vue.ts +10 -0
  3. package/dist/runtime/components/fields.vue.d.ts +10 -0
  4. package/dist/runtime/components/modal.vue +2 -2
  5. package/dist/runtime/components/ui/button-configurator/ButtonConfiguratorDialog.vue +3 -3
  6. package/dist/runtime/components/ui/command/CommandDialog.vue +3 -3
  7. package/dist/runtime/components/ui/dialog/DialogScrollContent.d.vue.ts +8 -3
  8. package/dist/runtime/components/ui/dialog/DialogScrollContent.vue +167 -14
  9. package/dist/runtime/components/ui/dialog/DialogScrollContent.vue.d.ts +8 -3
  10. package/dist/runtime/components/ui/fields/Fields.d.vue.ts +20 -0
  11. package/dist/runtime/components/ui/fields/Fields.vue +55 -3
  12. package/dist/runtime/components/ui/fields/Fields.vue.d.ts +20 -0
  13. package/dist/runtime/components/ui/fields/schema.d.ts +60 -0
  14. package/dist/runtime/components/ui/fields/schema.js +2 -0
  15. package/dist/runtime/components/ui/fields-configurator/FieldsConfiguratorDialog.d.vue.ts +10 -0
  16. package/dist/runtime/components/ui/fields-configurator/FieldsConfiguratorDialog.vue +76 -3
  17. package/dist/runtime/components/ui/fields-configurator/FieldsConfiguratorDialog.vue.d.ts +10 -0
  18. package/dist/runtime/components/ui/menu-tabs-configurator/MenuTabsConfiguratorDialog.vue +3 -3
  19. package/dist/runtime/components/ui/table-configurator/TableConfiguratorDialog.vue +3 -3
  20. package/dist/runtime/plugins/api/index.js +16 -0
  21. package/dist/runtime/plugins/cel/env.js +6 -0
  22. package/dist/runtime/plugins/cel/http-request.d.ts +16 -0
  23. package/dist/runtime/plugins/cel/http-request.js +36 -0
  24. package/package.json +1 -1
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@shwfed/nuxt",
3
3
  "configKey": "shwfed",
4
- "version": "0.11.35",
4
+ "version": "0.11.37",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "3.6.1"
@@ -171,6 +171,11 @@ declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
171
171
  message: string;
172
172
  }[] | undefined;
173
173
  maxCount?: string | undefined;
174
+ template?: string | undefined;
175
+ templateName?: readonly {
176
+ locale: "en" | "ja" | "ko" | "zh";
177
+ message: string;
178
+ }[] | undefined;
174
179
  style?: string | undefined;
175
180
  initialValue?: string | undefined;
176
181
  hidden?: string | undefined;
@@ -359,6 +364,11 @@ declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
359
364
  message: string;
360
365
  }[] | undefined;
361
366
  maxCount?: string | undefined;
367
+ template?: string | undefined;
368
+ templateName?: readonly {
369
+ locale: "en" | "ja" | "ko" | "zh";
370
+ message: string;
371
+ }[] | undefined;
362
372
  style?: string | undefined;
363
373
  initialValue?: string | undefined;
364
374
  hidden?: string | undefined;
@@ -171,6 +171,11 @@ declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
171
171
  message: string;
172
172
  }[] | undefined;
173
173
  maxCount?: string | undefined;
174
+ template?: string | undefined;
175
+ templateName?: readonly {
176
+ locale: "en" | "ja" | "ko" | "zh";
177
+ message: string;
178
+ }[] | undefined;
174
179
  style?: string | undefined;
175
180
  initialValue?: string | undefined;
176
181
  hidden?: string | undefined;
@@ -359,6 +364,11 @@ declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
359
364
  message: string;
360
365
  }[] | undefined;
361
366
  maxCount?: string | undefined;
367
+ template?: string | undefined;
368
+ templateName?: readonly {
369
+ locale: "en" | "ja" | "ko" | "zh";
370
+ message: string;
371
+ }[] | undefined;
362
372
  style?: string | undefined;
363
373
  initialValue?: string | undefined;
364
374
  hidden?: string | undefined;
@@ -4,10 +4,10 @@ import { computed, ref, useAttrs, useSlots } from "vue";
4
4
  import {
5
5
  Dialog,
6
6
  DialogClose,
7
- DialogContent,
8
7
  DialogDescription,
9
8
  DialogFooter,
10
9
  DialogHeader,
10
+ DialogScrollContent,
11
11
  DialogTitle,
12
12
  DialogTrigger
13
13
  } from "./ui/dialog";
@@ -39,7 +39,7 @@ const isDesktop = useMediaQuery(props.breakpoint);
39
39
  const desktopComponents = {
40
40
  Root: Dialog,
41
41
  Trigger: DialogTrigger,
42
- Content: DialogContent,
42
+ Content: DialogScrollContent,
43
43
  Header: DialogHeader,
44
44
  Title: DialogTitle,
45
45
  Description: DialogDescription,
@@ -13,7 +13,7 @@ import {
13
13
  ButtonsStyleC
14
14
  } from "../buttons/schema";
15
15
  import { Checkbox } from "../checkbox";
16
- import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "../dialog";
16
+ import { Dialog, DialogDescription, DialogFooter, DialogHeader, DialogScrollContent, DialogTitle } from "../dialog";
17
17
  import { IconPicker } from "../icon-picker";
18
18
  import { Input } from "../input";
19
19
  import Locale from "../locale/Locale.vue";
@@ -848,7 +848,7 @@ function confirmChanges() {
848
848
  :open="open"
849
849
  @update:open="open = $event"
850
850
  >
851
- <DialogContent class="flex h-[min(40rem,calc(100vh-4rem))] w-[calc(100%-2rem)] max-w-[calc(100%-2rem)] flex-col overflow-hidden p-0 sm:w-[72rem] sm:max-w-[72rem]">
851
+ <DialogScrollContent class="flex h-[min(40rem,calc(100vh-4rem))] w-[calc(100%-2rem)] max-w-[calc(100%-2rem)] flex-col overflow-hidden p-0 sm:w-[72rem] sm:max-w-[72rem]">
852
852
  <DialogHeader class="gap-1 border-b border-zinc-200 px-6 py-5">
853
853
  <div class="flex items-center gap-3">
854
854
  <DialogTitle class="text-xl font-semibold text-zinc-800">
@@ -1382,7 +1382,7 @@ function confirmChanges() {
1382
1382
  </Button>
1383
1383
  </div>
1384
1384
  </DialogFooter>
1385
- </DialogContent>
1385
+ </DialogScrollContent>
1386
1386
  </Dialog>
1387
1387
  </template>
1388
1388
 
@@ -1,6 +1,6 @@
1
1
  <script setup>
2
2
  import { useForwardPropsEmits } from "reka-ui";
3
- import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "../dialog";
3
+ import { Dialog, DialogDescription, DialogHeader, DialogScrollContent, DialogTitle } from "../dialog";
4
4
  import Command from "./Command.vue";
5
5
  const props = defineProps({
6
6
  open: { type: Boolean, required: false },
@@ -18,7 +18,7 @@ const forwarded = useForwardPropsEmits(props, emits);
18
18
  v-slot="slotProps"
19
19
  v-bind="forwarded"
20
20
  >
21
- <DialogContent
21
+ <DialogScrollContent
22
22
  :show-close-button="false"
23
23
  class="overflow-hidden p-0"
24
24
  >
@@ -29,6 +29,6 @@ const forwarded = useForwardPropsEmits(props, emits);
29
29
  <Command>
30
30
  <slot v-bind="slotProps" />
31
31
  </Command>
32
- </DialogContent>
32
+ </DialogScrollContent>
33
33
  </Dialog>
34
34
  </template>
@@ -2,10 +2,11 @@ import type { DialogContentProps } from 'reka-ui';
2
2
  import type { HTMLAttributes } from 'vue';
3
3
  type __VLS_Props = DialogContentProps & {
4
4
  class?: HTMLAttributes['class'];
5
+ showCloseButton?: boolean;
5
6
  };
6
- declare var __VLS_22: {};
7
+ declare var __VLS_23: {};
7
8
  type __VLS_Slots = {} & {
8
- default?: (props: typeof __VLS_22) => any;
9
+ default?: (props: typeof __VLS_23) => any;
9
10
  };
10
11
  declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
11
12
  escapeKeyDown: (event: KeyboardEvent) => any;
@@ -14,6 +15,7 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {},
14
15
  interactOutside: (event: import("reka-ui").PointerDownOutsideEvent | import("reka-ui").FocusOutsideEvent) => any;
15
16
  openAutoFocus: (event: Event) => any;
16
17
  closeAutoFocus: (event: Event) => any;
18
+ "after-close": () => any;
17
19
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
18
20
  onEscapeKeyDown?: ((event: KeyboardEvent) => any) | undefined;
19
21
  onPointerDownOutside?: ((event: import("reka-ui").PointerDownOutsideEvent) => any) | undefined;
@@ -21,7 +23,10 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {},
21
23
  onInteractOutside?: ((event: import("reka-ui").PointerDownOutsideEvent | import("reka-ui").FocusOutsideEvent) => any) | undefined;
22
24
  onOpenAutoFocus?: ((event: Event) => any) | undefined;
23
25
  onCloseAutoFocus?: ((event: Event) => any) | undefined;
24
- }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
26
+ "onAfter-close"?: (() => any) | undefined;
27
+ }>, {
28
+ showCloseButton: boolean;
29
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
25
30
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
26
31
  declare const _default: typeof __VLS_export;
27
32
  export default _default;
@@ -1,6 +1,13 @@
1
1
  <script setup>
2
- import { Icon } from "@iconify/vue";
3
2
  import { reactiveOmit } from "@vueuse/core";
3
+ import { Icon } from "@iconify/vue";
4
+ import {
5
+ computed,
6
+ onBeforeUnmount,
7
+ shallowRef,
8
+ useAttrs,
9
+ watchEffect
10
+ } from "vue";
4
11
  import {
5
12
  DialogClose,
6
13
  DialogContent,
@@ -17,11 +24,158 @@ const props = defineProps({
17
24
  disableOutsidePointerEvents: { type: Boolean, required: false },
18
25
  asChild: { type: Boolean, required: false },
19
26
  as: { type: null, required: false },
20
- class: { type: null, required: false }
27
+ class: { type: null, required: false },
28
+ showCloseButton: { type: Boolean, required: false, default: true }
29
+ });
30
+ const emit = defineEmits(["escapeKeyDown", "pointerDownOutside", "focusOutside", "interactOutside", "openAutoFocus", "closeAutoFocus", "after-close"]);
31
+ const attrs = useAttrs();
32
+ const delegatedProps = reactiveOmit(props, "class", "showCloseButton");
33
+ const forwarded = useForwardPropsEmits(delegatedProps, emit);
34
+ const contentElement = shallowRef(null);
35
+ let closeFallbackTimer;
36
+ let hasEmittedAfterClose = false;
37
+ function isClosedContentTarget(target) {
38
+ return target instanceof HTMLElement && target.dataset.state === "closed";
39
+ }
40
+ function isPointerDownOutsideEvent(event) {
41
+ if (!("detail" in event) || typeof event.detail !== "object" || event.detail === null) {
42
+ return false;
43
+ }
44
+ if (!("originalEvent" in event.detail)) {
45
+ return false;
46
+ }
47
+ return event.detail.originalEvent instanceof MouseEvent;
48
+ }
49
+ function clearCloseFallbackTimer() {
50
+ if (closeFallbackTimer === void 0) {
51
+ return;
52
+ }
53
+ window.clearTimeout(closeFallbackTimer);
54
+ closeFallbackTimer = void 0;
55
+ }
56
+ function emitAfterClose() {
57
+ if (hasEmittedAfterClose) {
58
+ return;
59
+ }
60
+ hasEmittedAfterClose = true;
61
+ clearCloseFallbackTimer();
62
+ emit("after-close");
63
+ }
64
+ function parseDurationMs(value) {
65
+ const normalizedValue = value.trim();
66
+ if (normalizedValue.endsWith("ms")) {
67
+ const duration = Number.parseFloat(normalizedValue.slice(0, -2));
68
+ return Number.isFinite(duration) ? duration : 0;
69
+ }
70
+ if (normalizedValue.endsWith("s")) {
71
+ const duration = Number.parseFloat(normalizedValue.slice(0, -1));
72
+ return Number.isFinite(duration) ? duration * 1e3 : 0;
73
+ }
74
+ return 0;
75
+ }
76
+ function getLongestMotionMs(element) {
77
+ const style = window.getComputedStyle(element);
78
+ const animationDurations = style.animationDuration.split(",");
79
+ const animationDelays = style.animationDelay.split(",");
80
+ const transitionDurations = style.transitionDuration.split(",");
81
+ const transitionDelays = style.transitionDelay.split(",");
82
+ let longestMotionMs = 0;
83
+ for (const [index, durationValue] of animationDurations.entries()) {
84
+ const durationMs = parseDurationMs(durationValue);
85
+ const delayMs = parseDurationMs(animationDelays[index] ?? animationDelays[0] ?? "0s");
86
+ longestMotionMs = Math.max(longestMotionMs, durationMs + delayMs);
87
+ }
88
+ for (const [index, durationValue] of transitionDurations.entries()) {
89
+ const durationMs = parseDurationMs(durationValue);
90
+ const delayMs = parseDurationMs(transitionDelays[index] ?? transitionDelays[0] ?? "0s");
91
+ longestMotionMs = Math.max(longestMotionMs, durationMs + delayMs);
92
+ }
93
+ return longestMotionMs;
94
+ }
95
+ function syncCloseFallback() {
96
+ const element = contentElement.value;
97
+ clearCloseFallbackTimer();
98
+ if (!element) {
99
+ return;
100
+ }
101
+ if (element.dataset.state !== "closed") {
102
+ hasEmittedAfterClose = false;
103
+ return;
104
+ }
105
+ const motionMs = getLongestMotionMs(element);
106
+ closeFallbackTimer = window.setTimeout(() => {
107
+ emitAfterClose();
108
+ }, motionMs > 0 ? motionMs : 0);
109
+ }
110
+ function handleCloseMotionEnd(event) {
111
+ if (hasEmittedAfterClose || event.target !== event.currentTarget || !isClosedContentTarget(event.currentTarget)) {
112
+ return;
113
+ }
114
+ emitAfterClose();
115
+ }
116
+ function setContentElement(target) {
117
+ if (target instanceof HTMLElement) {
118
+ contentElement.value = target;
119
+ syncCloseFallback();
120
+ return;
121
+ }
122
+ if (target && "$el" in target && target.$el instanceof HTMLElement) {
123
+ contentElement.value = target.$el;
124
+ syncCloseFallback();
125
+ return;
126
+ }
127
+ contentElement.value = null;
128
+ clearCloseFallbackTimer();
129
+ }
130
+ function callEventHandlers(eventHandler, event) {
131
+ if (typeof eventHandler === "function") {
132
+ eventHandler(event);
133
+ return;
134
+ }
135
+ if (!Array.isArray(eventHandler)) {
136
+ return;
137
+ }
138
+ for (const handler of eventHandler) {
139
+ if (typeof handler === "function") {
140
+ handler(event);
141
+ }
142
+ }
143
+ }
144
+ function handlePointerDownOutside(event) {
145
+ if (isPointerDownOutsideEvent(event) && event.detail.originalEvent.target instanceof HTMLElement) {
146
+ const target = event.detail.originalEvent.target;
147
+ if (event.detail.originalEvent.offsetX > target.clientWidth || event.detail.originalEvent.offsetY > target.clientHeight) {
148
+ event.preventDefault();
149
+ }
150
+ }
151
+ callEventHandlers(Reflect.get(forwarded, "onPointerDownOutside"), event);
152
+ callEventHandlers(Reflect.get(attrs, "onPointerDownOutside"), event);
153
+ }
154
+ const contentBindings = computed(() => ({
155
+ ...attrs,
156
+ ...forwarded,
157
+ onPointerDownOutside: handlePointerDownOutside
158
+ }));
159
+ watchEffect((onCleanup) => {
160
+ const element = contentElement.value;
161
+ if (!element) {
162
+ return;
163
+ }
164
+ const observer = new MutationObserver(() => {
165
+ syncCloseFallback();
166
+ });
167
+ observer.observe(element, {
168
+ attributes: true,
169
+ attributeFilter: ["data-state"]
170
+ });
171
+ syncCloseFallback();
172
+ onCleanup(() => {
173
+ observer.disconnect();
174
+ });
175
+ });
176
+ onBeforeUnmount(() => {
177
+ clearCloseFallbackTimer();
21
178
  });
22
- const emits = defineEmits(["escapeKeyDown", "pointerDownOutside", "focusOutside", "interactOutside", "openAutoFocus", "closeAutoFocus"]);
23
- const delegatedProps = reactiveOmit(props, "class");
24
- const forwarded = useForwardPropsEmits(delegatedProps, emits);
25
179
  </script>
26
180
 
27
181
  <template>
@@ -30,25 +184,24 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
30
184
  class="fixed inset-0 z-50 grid place-items-center overflow-y-auto bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0"
31
185
  >
32
186
  <DialogContent
187
+ :ref="setContentElement"
188
+ data-slot="dialog-content"
33
189
  :class="
34
190
  cn(
35
191
  'relative z-50 grid w-full max-w-lg my-8 gap-4 border border-zinc-200 bg-white p-6 shadow-lg duration-200 sm:rounded-lg md:w-full',
36
192
  props.class
37
193
  )
38
194
  "
39
- v-bind="{ ...$attrs, ...forwarded }"
40
- @pointer-down-outside="(event) => {
41
- const originalEvent = event.detail.originalEvent;
42
- const target = originalEvent.target;
43
- if (originalEvent.offsetX > target.clientWidth || originalEvent.offsetY > target.clientHeight) {
44
- event.preventDefault();
45
- }
46
- }"
195
+ v-bind="contentBindings"
196
+ @animationend="handleCloseMotionEnd"
197
+ @transitionend="handleCloseMotionEnd"
47
198
  >
48
199
  <slot />
49
200
 
50
201
  <DialogClose
51
- class="absolute top-4 right-4 p-0.5 transition-colors rounded-md hover:bg-zinc-100"
202
+ v-if="showCloseButton"
203
+ data-slot="dialog-close"
204
+ class="data-[state=open]:bg-zinc-100 data-[state=open]:text-zinc-700 absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
52
205
  >
53
206
  <Icon icon="fluent:dismiss-20-filled" />
54
207
  <span class="sr-only">Close</span>
@@ -2,10 +2,11 @@ import type { DialogContentProps } from 'reka-ui';
2
2
  import type { HTMLAttributes } from 'vue';
3
3
  type __VLS_Props = DialogContentProps & {
4
4
  class?: HTMLAttributes['class'];
5
+ showCloseButton?: boolean;
5
6
  };
6
- declare var __VLS_22: {};
7
+ declare var __VLS_23: {};
7
8
  type __VLS_Slots = {} & {
8
- default?: (props: typeof __VLS_22) => any;
9
+ default?: (props: typeof __VLS_23) => any;
9
10
  };
10
11
  declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
11
12
  escapeKeyDown: (event: KeyboardEvent) => any;
@@ -14,6 +15,7 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {},
14
15
  interactOutside: (event: import("reka-ui").PointerDownOutsideEvent | import("reka-ui").FocusOutsideEvent) => any;
15
16
  openAutoFocus: (event: Event) => any;
16
17
  closeAutoFocus: (event: Event) => any;
18
+ "after-close": () => any;
17
19
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
18
20
  onEscapeKeyDown?: ((event: KeyboardEvent) => any) | undefined;
19
21
  onPointerDownOutside?: ((event: import("reka-ui").PointerDownOutsideEvent) => any) | undefined;
@@ -21,7 +23,10 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {},
21
23
  onInteractOutside?: ((event: import("reka-ui").PointerDownOutsideEvent | import("reka-ui").FocusOutsideEvent) => any) | undefined;
22
24
  onOpenAutoFocus?: ((event: Event) => any) | undefined;
23
25
  onCloseAutoFocus?: ((event: Event) => any) | undefined;
24
- }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
26
+ "onAfter-close"?: (() => any) | undefined;
27
+ }>, {
28
+ showCloseButton: boolean;
29
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
25
30
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
26
31
  declare const _default: typeof __VLS_export;
27
32
  export default _default;
@@ -165,6 +165,11 @@ declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
165
165
  message: string;
166
166
  }[] | undefined;
167
167
  maxCount?: string | undefined;
168
+ template?: string | undefined;
169
+ templateName?: readonly {
170
+ locale: "en" | "ja" | "ko" | "zh";
171
+ message: string;
172
+ }[] | undefined;
168
173
  style?: string | undefined;
169
174
  initialValue?: string | undefined;
170
175
  hidden?: string | undefined;
@@ -352,6 +357,11 @@ declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
352
357
  message: string;
353
358
  }[] | undefined;
354
359
  maxCount?: string | undefined;
360
+ template?: string | undefined;
361
+ templateName?: readonly {
362
+ locale: "en" | "ja" | "ko" | "zh";
363
+ message: string;
364
+ }[] | undefined;
355
365
  style?: string | undefined;
356
366
  initialValue?: string | undefined;
357
367
  hidden?: string | undefined;
@@ -535,6 +545,11 @@ declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
535
545
  message: string;
536
546
  }[] | undefined;
537
547
  maxCount?: string | undefined;
548
+ template?: string | undefined;
549
+ templateName?: readonly {
550
+ locale: "en" | "ja" | "ko" | "zh";
551
+ message: string;
552
+ }[] | undefined;
538
553
  style?: string | undefined;
539
554
  initialValue?: string | undefined;
540
555
  hidden?: string | undefined;
@@ -720,6 +735,11 @@ declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
720
735
  message: string;
721
736
  }[] | undefined;
722
737
  maxCount?: string | undefined;
738
+ template?: string | undefined;
739
+ templateName?: readonly {
740
+ locale: "en" | "ja" | "ko" | "zh";
741
+ message: string;
742
+ }[] | undefined;
723
743
  style?: string | undefined;
724
744
  initialValue?: string | undefined;
725
745
  hidden?: string | undefined;
@@ -226,6 +226,24 @@ function getFileIcon(filename) {
226
226
  const ext = filename.split(".").pop()?.toLowerCase() ?? "";
227
227
  return FILE_EXTENSION_ICONS[ext] ?? "vscode-icons:default-file";
228
228
  }
229
+ const MIME_TO_ICON = {
230
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "vscode-icons:file-type-excel",
231
+ "application/vnd.ms-excel": "vscode-icons:file-type-excel",
232
+ "application/pdf": "vscode-icons:file-type-pdf2",
233
+ "application/msword": "vscode-icons:file-type-word",
234
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document": "vscode-icons:file-type-word",
235
+ "application/vnd.ms-powerpoint": "vscode-icons:file-type-powerpoint",
236
+ "application/vnd.openxmlformats-officedocument.presentationml.presentation": "vscode-icons:file-type-powerpoint",
237
+ "application/x-zip-compressed": "vscode-icons:file-type-zip",
238
+ "application/zip": "vscode-icons:file-type-zip",
239
+ "image/png": "vscode-icons:file-type-image",
240
+ "image/jpg": "vscode-icons:file-type-image",
241
+ "image/jpeg": "vscode-icons:file-type-image"
242
+ };
243
+ function getUploadTemplateIcon(field) {
244
+ const first = field.accept?.[0];
245
+ return (first && MIME_TO_ICON[first]) ?? "fluent:arrow-download-20-regular";
246
+ }
229
247
  function getUploadAcceptString(field) {
230
248
  if (!field.accept || field.accept.length === 0) {
231
249
  return void 0;
@@ -298,6 +316,24 @@ function handleUploadDrop(field, event) {
298
316
  function handleUploadDragOver(event) {
299
317
  event.preventDefault();
300
318
  }
319
+ const templateDownloading = ref({});
320
+ async function handleTemplateDownload(field) {
321
+ if (!field.template || templateDownloading.value[field.id]) return;
322
+ templateDownloading.value[field.id] = true;
323
+ try {
324
+ const file = await $dsl.evaluate`${field.template}`();
325
+ const url = URL.createObjectURL(file);
326
+ const a = document.createElement("a");
327
+ a.href = url;
328
+ a.download = file.name;
329
+ a.click();
330
+ URL.revokeObjectURL(url);
331
+ } catch (e) {
332
+ console.error("[Fields] template download failed:", e);
333
+ } finally {
334
+ templateDownloading.value[field.id] = false;
335
+ }
336
+ }
301
337
  function stringifySelectValue(value) {
302
338
  try {
303
339
  return JSON.stringify(value);
@@ -673,6 +709,19 @@ export {
673
709
  :icon="field.icon ?? 'fluent:cloud-arrow-up-20-regular'"
674
710
  class="text-4xl text-zinc-400"
675
711
  />
712
+ <button
713
+ v-if="field.template"
714
+ type="button"
715
+ class="inline-flex items-center gap-1.5 rounded-md border border-zinc-300 bg-white px-3 py-1.5 text-sm text-zinc-600 transition-colors hover:border-[--el-color-primary] hover:text-[--el-color-primary] disabled:opacity-50"
716
+ :disabled="templateDownloading[field.id]"
717
+ @click.prevent.stop="handleTemplateDownload(field)"
718
+ >
719
+ <Icon
720
+ :icon="templateDownloading[field.id] ? 'svg-spinners:ring-resize' : getUploadTemplateIcon(field)"
721
+ class="text-base"
722
+ />
723
+ {{ getLocalizedText(field.templateName, locale) ?? t("upload-download-template") }}
724
+ </button>
676
725
  <p
677
726
  v-if="field.description"
678
727
  class="text-sm text-zinc-600"
@@ -1105,7 +1154,8 @@ export {
1105
1154
  "upload-click-or-drag": "点击或拖拽文件到此处上传",
1106
1155
  "upload-accept-description": "仅接受 {formats} 格式文件",
1107
1156
  "upload-accept-or": "或",
1108
- "upload-accept-all": "接受所有格式文件"
1157
+ "upload-accept-all": "接受所有格式文件",
1158
+ "upload-download-template": "下载模板"
1109
1159
  },
1110
1160
  "ja": {
1111
1161
  "clear": "クリア",
@@ -1115,7 +1165,8 @@ export {
1115
1165
  "upload-click-or-drag": "クリックまたはファイルをここにドラッグしてアップロード",
1116
1166
  "upload-accept-description": "{formats} 形式のファイルのみ受け付けます",
1117
1167
  "upload-accept-or": "または",
1118
- "upload-accept-all": "すべての形式を受け付けます"
1168
+ "upload-accept-all": "すべての形式を受け付けます",
1169
+ "upload-download-template": "テンプレートをダウンロード"
1119
1170
  },
1120
1171
  "en": {
1121
1172
  "clear": "Clear",
@@ -1125,7 +1176,8 @@ export {
1125
1176
  "upload-click-or-drag": "Click or drag files here to upload",
1126
1177
  "upload-accept-description": "Only {formats} format files accepted",
1127
1178
  "upload-accept-or": "or",
1128
- "upload-accept-all": "All formats accepted"
1179
+ "upload-accept-all": "All formats accepted",
1180
+ "upload-download-template": "Download Template"
1129
1181
  }
1130
1182
  }
1131
1183
  </i18n>
@@ -165,6 +165,11 @@ declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
165
165
  message: string;
166
166
  }[] | undefined;
167
167
  maxCount?: string | undefined;
168
+ template?: string | undefined;
169
+ templateName?: readonly {
170
+ locale: "en" | "ja" | "ko" | "zh";
171
+ message: string;
172
+ }[] | undefined;
168
173
  style?: string | undefined;
169
174
  initialValue?: string | undefined;
170
175
  hidden?: string | undefined;
@@ -352,6 +357,11 @@ declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
352
357
  message: string;
353
358
  }[] | undefined;
354
359
  maxCount?: string | undefined;
360
+ template?: string | undefined;
361
+ templateName?: readonly {
362
+ locale: "en" | "ja" | "ko" | "zh";
363
+ message: string;
364
+ }[] | undefined;
355
365
  style?: string | undefined;
356
366
  initialValue?: string | undefined;
357
367
  hidden?: string | undefined;
@@ -535,6 +545,11 @@ declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
535
545
  message: string;
536
546
  }[] | undefined;
537
547
  maxCount?: string | undefined;
548
+ template?: string | undefined;
549
+ templateName?: readonly {
550
+ locale: "en" | "ja" | "ko" | "zh";
551
+ message: string;
552
+ }[] | undefined;
538
553
  style?: string | undefined;
539
554
  initialValue?: string | undefined;
540
555
  hidden?: string | undefined;
@@ -720,6 +735,11 @@ declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
720
735
  message: string;
721
736
  }[] | undefined;
722
737
  maxCount?: string | undefined;
738
+ template?: string | undefined;
739
+ templateName?: readonly {
740
+ locale: "en" | "ja" | "ko" | "zh";
741
+ message: string;
742
+ }[] | undefined;
723
743
  style?: string | undefined;
724
744
  initialValue?: string | undefined;
725
745
  hidden?: string | undefined;
@@ -217,6 +217,16 @@ export declare const UploadFieldC: z.ZodObject<{
217
217
  message: z.ZodString;
218
218
  }, z.core.$strip>>>>;
219
219
  maxCount: z.ZodOptional<z.ZodString>;
220
+ template: z.ZodOptional<z.ZodString>;
221
+ templateName: z.ZodOptional<z.ZodReadonly<z.ZodArray<z.ZodObject<{
222
+ locale: z.ZodEnum<{
223
+ en: "en";
224
+ ja: "ja";
225
+ ko: "ko";
226
+ zh: "zh";
227
+ }>;
228
+ message: z.ZodString;
229
+ }, z.core.$strip>>>>;
220
230
  style: z.ZodOptional<z.ZodString>;
221
231
  initialValue: z.ZodOptional<z.ZodString>;
222
232
  hidden: z.ZodOptional<z.ZodString>;
@@ -437,6 +447,16 @@ export declare const FieldC: z.ZodDiscriminatedUnion<[z.ZodObject<{
437
447
  message: z.ZodString;
438
448
  }, z.core.$strip>>>>;
439
449
  maxCount: z.ZodOptional<z.ZodString>;
450
+ template: z.ZodOptional<z.ZodString>;
451
+ templateName: z.ZodOptional<z.ZodReadonly<z.ZodArray<z.ZodObject<{
452
+ locale: z.ZodEnum<{
453
+ en: "en";
454
+ ja: "ja";
455
+ ko: "ko";
456
+ zh: "zh";
457
+ }>;
458
+ message: z.ZodString;
459
+ }, z.core.$strip>>>>;
440
460
  style: z.ZodOptional<z.ZodString>;
441
461
  initialValue: z.ZodOptional<z.ZodString>;
442
462
  hidden: z.ZodOptional<z.ZodString>;
@@ -671,6 +691,16 @@ export declare const FieldsBodyC: z.ZodReadonly<z.ZodObject<{
671
691
  message: z.ZodString;
672
692
  }, z.core.$strip>>>>;
673
693
  maxCount: z.ZodOptional<z.ZodString>;
694
+ template: z.ZodOptional<z.ZodString>;
695
+ templateName: z.ZodOptional<z.ZodReadonly<z.ZodArray<z.ZodObject<{
696
+ locale: z.ZodEnum<{
697
+ en: "en";
698
+ ja: "ja";
699
+ ko: "ko";
700
+ zh: "zh";
701
+ }>;
702
+ message: z.ZodString;
703
+ }, z.core.$strip>>>>;
674
704
  style: z.ZodOptional<z.ZodString>;
675
705
  initialValue: z.ZodOptional<z.ZodString>;
676
706
  hidden: z.ZodOptional<z.ZodString>;
@@ -899,6 +929,16 @@ export declare const FieldsBodyInputC: z.ZodReadonly<z.ZodObject<{
899
929
  message: z.ZodString;
900
930
  }, z.core.$strip>>>>;
901
931
  maxCount: z.ZodOptional<z.ZodString>;
932
+ template: z.ZodOptional<z.ZodString>;
933
+ templateName: z.ZodOptional<z.ZodReadonly<z.ZodArray<z.ZodObject<{
934
+ locale: z.ZodEnum<{
935
+ en: "en";
936
+ ja: "ja";
937
+ ko: "ko";
938
+ zh: "zh";
939
+ }>;
940
+ message: z.ZodString;
941
+ }, z.core.$strip>>>>;
902
942
  style: z.ZodOptional<z.ZodString>;
903
943
  initialValue: z.ZodOptional<z.ZodString>;
904
944
  hidden: z.ZodOptional<z.ZodString>;
@@ -1127,6 +1167,16 @@ export declare const FieldsConfigC: z.ZodReadonly<z.ZodObject<{
1127
1167
  message: z.ZodString;
1128
1168
  }, z.core.$strip>>>>;
1129
1169
  maxCount: z.ZodOptional<z.ZodString>;
1170
+ template: z.ZodOptional<z.ZodString>;
1171
+ templateName: z.ZodOptional<z.ZodReadonly<z.ZodArray<z.ZodObject<{
1172
+ locale: z.ZodEnum<{
1173
+ en: "en";
1174
+ ja: "ja";
1175
+ ko: "ko";
1176
+ zh: "zh";
1177
+ }>;
1178
+ message: z.ZodString;
1179
+ }, z.core.$strip>>>>;
1130
1180
  style: z.ZodOptional<z.ZodString>;
1131
1181
  initialValue: z.ZodOptional<z.ZodString>;
1132
1182
  hidden: z.ZodOptional<z.ZodString>;
@@ -1357,6 +1407,16 @@ export declare const FieldsConfigInputC: z.ZodReadonly<z.ZodObject<{
1357
1407
  message: z.ZodString;
1358
1408
  }, z.core.$strip>>>>;
1359
1409
  maxCount: z.ZodOptional<z.ZodString>;
1410
+ template: z.ZodOptional<z.ZodString>;
1411
+ templateName: z.ZodOptional<z.ZodReadonly<z.ZodArray<z.ZodObject<{
1412
+ locale: z.ZodEnum<{
1413
+ en: "en";
1414
+ ja: "ja";
1415
+ ko: "ko";
1416
+ zh: "zh";
1417
+ }>;
1418
+ message: z.ZodString;
1419
+ }, z.core.$strip>>>>;
1360
1420
  style: z.ZodOptional<z.ZodString>;
1361
1421
  initialValue: z.ZodOptional<z.ZodString>;
1362
1422
  hidden: z.ZodOptional<z.ZodString>;
@@ -133,6 +133,8 @@ export const UploadFieldC = z.object({
133
133
  accept: z.array(z.string()).readonly().optional().describe("\u5141\u8BB8\u4E0A\u4F20\u7684 MIME \u7C7B\u578B\u6570\u7EC4\uFF0C\u540C\u65F6\u63A7\u5236\u6587\u4EF6\u9009\u62E9\u8FC7\u6EE4\u548C\u533A\u57DF\u63CF\u8FF0\u6587\u5B57"),
134
134
  description: localeC.optional().describe("\u4E0A\u4F20\u533A\u57DF\u7684\u63D0\u793A\u6587\u672C\u7684\u672C\u5730\u5316\u663E\u793A\u6587\u672C\uFF0C\u66FF\u6362\u9ED8\u8BA4\u7684\u300C\u70B9\u51FB\u6216\u62D6\u62FD\u5230\u6B64\u5904\u4E0A\u4F20\u300D"),
135
135
  maxCount: expressionC(["int", "dyn"], inheritedFieldContext).optional().describe("\u8FD4\u56DE\u6574\u6570\u7684 CEL \u8868\u8FBE\u5F0F\uFF0C\u9650\u5236\u6700\u5927\u53EF\u4E0A\u4F20\u6587\u4EF6\u6570\u91CF\uFF0C\u53EF\u4F7F\u7528 form\u3001row\u3001index \u548C id \u53D8\u91CF"),
136
+ template: expressionC(["File", "dyn"]).optional().describe('\u8FD4\u56DE File \u7684 CEL \u8868\u8FBE\u5F0F\uFF08\u5982 http.post("/url").file()\uFF09\uFF0C\u914D\u7F6E\u540E\u5728\u4E0A\u4F20\u533A\u57DF\u663E\u793A\u300C\u4E0B\u8F7D\u6A21\u677F\u300D\u6309\u94AE'),
137
+ templateName: localeC.optional().describe("\u4E0B\u8F7D\u6A21\u677F\u6309\u94AE\u7684\u672C\u5730\u5316\u663E\u793A\u6587\u672C\uFF0C\u7559\u7A7A\u65F6\u663E\u793A\u9ED8\u8BA4\u6587\u672C\u300C\u4E0B\u8F7D\u6A21\u677F\u300D"),
136
138
  style: z.string().optional().describe("CSS \u6837\u5F0F\u5BF9\u8C61\u8868\u8FBE\u5F0F\uFF0C\u63A7\u5236\u5B57\u6BB5\u7684\u5E03\u5C40\u4E0E\u5916\u89C2"),
137
139
  initialValue: expressionC(["list(dyn)", "dyn"], inheritedFieldContext).optional().describe("\u8FD4\u56DE\u6570\u7EC4\u7684 CEL \u8868\u8FBE\u5F0F\uFF0C\u5728\u5B57\u6BB5\u9996\u6B21\u521D\u59CB\u5316\u4E14 path \u4E0D\u5B58\u5728\u65F6\u5199\u5165\u521D\u59CB\u503C\uFF0C\u53EF\u4F7F\u7528 form\u3001row\u3001index \u548C id \u53D8\u91CF"),
138
140
  hidden: expressionC(["bool", "dyn"], inheritedFieldContext).optional().describe("\u4E3A true \u65F6\uFF0C\u9690\u85CF\u8FD9\u4E2A\u5B57\u6BB5\uFF0C\u53EF\u4F7F\u7528 form\u3001row\u3001index \u548C id \u53D8\u91CF"),
@@ -166,6 +166,11 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {},
166
166
  message: string;
167
167
  }[] | undefined;
168
168
  maxCount?: string | undefined;
169
+ template?: string | undefined;
170
+ templateName?: readonly {
171
+ locale: "en" | "ja" | "ko" | "zh";
172
+ message: string;
173
+ }[] | undefined;
169
174
  style?: string | undefined;
170
175
  initialValue?: string | undefined;
171
176
  hidden?: string | undefined;
@@ -349,6 +354,11 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {},
349
354
  message: string;
350
355
  }[] | undefined;
351
356
  maxCount?: string | undefined;
357
+ template?: string | undefined;
358
+ templateName?: readonly {
359
+ locale: "en" | "ja" | "ko" | "zh";
360
+ message: string;
361
+ }[] | undefined;
352
362
  style?: string | undefined;
353
363
  initialValue?: string | undefined;
354
364
  hidden?: string | undefined;
@@ -23,10 +23,10 @@ import { cn } from "../../../utils/cn";
23
23
  import { Button } from "../button";
24
24
  import {
25
25
  Dialog,
26
- DialogContent,
27
26
  DialogDescription,
28
27
  DialogFooter,
29
28
  DialogHeader,
29
+ DialogScrollContent,
30
30
  DialogTitle
31
31
  } from "../dialog";
32
32
  import {
@@ -301,6 +301,7 @@ function normalizeField(field) {
301
301
  contentStyle: normalizeOptionalString(field.contentStyle ?? ""),
302
302
  accept: normalizedAccept.length > 0 ? normalizedAccept : void 0,
303
303
  maxCount: normalizeOptionalString(field.maxCount ?? ""),
304
+ template: normalizeOptionalString(field.template ?? ""),
304
305
  initialValue: normalizeOptionalString(field.initialValue ?? ""),
305
306
  hidden: normalizeOptionalString(field.hidden ?? ""),
306
307
  disabled: normalizeOptionalString(field.disabled ?? ""),
@@ -907,6 +908,22 @@ function updateSelectedUploadDescription(value) {
907
908
  };
908
909
  });
909
910
  }
911
+ function updateSelectedUploadTemplateName(value) {
912
+ const selected = selectedField.value;
913
+ if (!selected || selected.field.type !== "upload") {
914
+ return;
915
+ }
916
+ clearFieldError(selected.draftId, "templateName");
917
+ updateDraftField(selected.draftId, (field) => {
918
+ if (field.type !== "upload") {
919
+ return field;
920
+ }
921
+ return {
922
+ ...field,
923
+ templateName: value
924
+ };
925
+ });
926
+ }
910
927
  function updateSelectedUploadMaxCount(value) {
911
928
  const selected = selectedField.value;
912
929
  if (!selected || selected.field.type !== "upload") {
@@ -923,6 +940,22 @@ function updateSelectedUploadMaxCount(value) {
923
940
  };
924
941
  });
925
942
  }
943
+ function updateSelectedUploadTemplate(value) {
944
+ const selected = selectedField.value;
945
+ if (!selected || selected.field.type !== "upload") {
946
+ return;
947
+ }
948
+ clearFieldError(selected.draftId, "template");
949
+ updateDraftField(selected.draftId, (field) => {
950
+ if (field.type !== "upload") {
951
+ return field;
952
+ }
953
+ return {
954
+ ...field,
955
+ template: normalizeOptionalString(String(value))
956
+ };
957
+ });
958
+ }
926
959
  function addValidationRule() {
927
960
  const selected = selectedField.value;
928
961
  if (!selected || isPassiveField(selected.field)) {
@@ -1434,7 +1467,7 @@ function confirmChanges() {
1434
1467
  :open="open"
1435
1468
  @update:open="handleOpenChange"
1436
1469
  >
1437
- <DialogContent
1470
+ <DialogScrollContent
1438
1471
  class="flex h-[min(42rem,calc(100vh-4rem))] w-[calc(100%-2rem)] max-h-[calc(100vh-4rem)] max-w-[calc(100%-2rem)] flex-col overflow-hidden p-0 sm:w-[80vw] sm:max-w-[80vw]"
1439
1472
  :show-close-button="true"
1440
1473
  >
@@ -2298,6 +2331,37 @@ function confirmChanges() {
2298
2331
  {{ validationErrors[getFieldErrorKey(selectedField.draftId, "maxCount")] }}
2299
2332
  </p>
2300
2333
  </label>
2334
+
2335
+ <label class="flex flex-col gap-2 md:col-span-2">
2336
+ <span class="text-xs font-medium text-zinc-500">
2337
+ {{ t("field-upload-template") }}
2338
+ </span>
2339
+ <Textarea
2340
+ data-slot="fields-configurator-field-upload-template-input"
2341
+ :model-value="selectedField.field.template ?? ''"
2342
+ :aria-invalid="validationErrors[getFieldErrorKey(selectedField.draftId, 'template')] ? 'true' : void 0"
2343
+ :placeholder="t('field-upload-template-placeholder')"
2344
+ class="min-h-20 font-mono text-sm"
2345
+ @update:model-value="updateSelectedUploadTemplate"
2346
+ />
2347
+ <p
2348
+ v-if="validationErrors[getFieldErrorKey(selectedField.draftId, 'template')]"
2349
+ class="text-xs text-red-500"
2350
+ >
2351
+ {{ validationErrors[getFieldErrorKey(selectedField.draftId, "template")] }}
2352
+ </p>
2353
+ </label>
2354
+
2355
+ <div class="flex flex-col gap-2 md:col-span-2">
2356
+ <span class="text-xs font-medium text-zinc-500">
2357
+ {{ t("field-upload-template-name") }}
2358
+ </span>
2359
+ <Locale
2360
+ data-slot="fields-configurator-field-upload-template-name-locale"
2361
+ :model-value="selectedField.field.templateName"
2362
+ @update:model-value="updateSelectedUploadTemplateName"
2363
+ />
2364
+ </div>
2301
2365
  </section>
2302
2366
 
2303
2367
  <section
@@ -2478,7 +2542,7 @@ function confirmChanges() {
2478
2542
  </Button>
2479
2543
  </div>
2480
2544
  </DialogFooter>
2481
- </DialogContent>
2545
+ </DialogScrollContent>
2482
2546
  </Dialog>
2483
2547
  </template>
2484
2548
 
@@ -2582,6 +2646,9 @@ function confirmChanges() {
2582
2646
  "field-upload-description": "上传提示文本",
2583
2647
  "field-upload-max-count": "最大文件数量",
2584
2648
  "field-upload-max-count-placeholder": "例如 5",
2649
+ "field-upload-template": "下载模板表达式",
2650
+ "field-upload-template-placeholder": "例如 http.post(\"/api/v1/example/download_template\").file()",
2651
+ "field-upload-template-name": "下载模板按钮名称",
2585
2652
  "field-validation": "校验规则",
2586
2653
  "field-validation-description": "字段失焦时按顺序执行,命中第一个失败规则后停止。",
2587
2654
  "add-validation-rule": "新增规则",
@@ -2699,6 +2766,9 @@ function confirmChanges() {
2699
2766
  "field-upload-description": "アップロード説明文",
2700
2767
  "field-upload-max-count": "最大ファイル数",
2701
2768
  "field-upload-max-count-placeholder": "例: 5",
2769
+ "field-upload-template": "テンプレートダウンロード式",
2770
+ "field-upload-template-placeholder": "例: http.post(\"/api/v1/example/download_template\").file()",
2771
+ "field-upload-template-name": "テンプレートボタン名",
2702
2772
  "field-validation": "検証ルール",
2703
2773
  "field-validation-description": "フォーカスを外したときに順番に評価し、最初の失敗で停止します。",
2704
2774
  "add-validation-rule": "ルールを追加",
@@ -2816,6 +2886,9 @@ function confirmChanges() {
2816
2886
  "field-upload-description": "Upload description",
2817
2887
  "field-upload-max-count": "Max file count",
2818
2888
  "field-upload-max-count-placeholder": "For example 5",
2889
+ "field-upload-template": "Template download expression",
2890
+ "field-upload-template-placeholder": "e.g. http.post(\"/api/v1/example/download_template\").file()",
2891
+ "field-upload-template-name": "Template button name",
2819
2892
  "field-validation": "Validation rules",
2820
2893
  "field-validation-description": "Rules run on blur in order and stop at the first failure.",
2821
2894
  "add-validation-rule": "Add rule",
@@ -166,6 +166,11 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {},
166
166
  message: string;
167
167
  }[] | undefined;
168
168
  maxCount?: string | undefined;
169
+ template?: string | undefined;
170
+ templateName?: readonly {
171
+ locale: "en" | "ja" | "ko" | "zh";
172
+ message: string;
173
+ }[] | undefined;
169
174
  style?: string | undefined;
170
175
  initialValue?: string | undefined;
171
176
  hidden?: string | undefined;
@@ -349,6 +354,11 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {},
349
354
  message: string;
350
355
  }[] | undefined;
351
356
  maxCount?: string | undefined;
357
+ template?: string | undefined;
358
+ templateName?: readonly {
359
+ locale: "en" | "ja" | "ko" | "zh";
360
+ message: string;
361
+ }[] | undefined;
352
362
  style?: string | undefined;
353
363
  initialValue?: string | undefined;
354
364
  hidden?: string | undefined;
@@ -7,10 +7,10 @@ import { cn } from "../../../utils/cn";
7
7
  import { Button } from "../button";
8
8
  import {
9
9
  Dialog,
10
- DialogContent,
11
10
  DialogDescription,
12
11
  DialogFooter,
13
12
  DialogHeader,
13
+ DialogScrollContent,
14
14
  DialogTitle
15
15
  } from "../dialog";
16
16
  import { ExpressionEditor } from "../expression-editor";
@@ -258,7 +258,7 @@ watch(normalizedSearch, async () => {
258
258
  :open="open"
259
259
  @update:open="open = $event"
260
260
  >
261
- <DialogContent
261
+ <DialogScrollContent
262
262
  data-slot="menu-tabs-configurator-dialog"
263
263
  class="flex h-[min(42rem,calc(100vh-4rem))] w-[calc(100%-2rem)] max-h-[calc(100vh-4rem)] max-w-[calc(100%-2rem)] flex-col overflow-hidden p-0 sm:w-[80vw] sm:max-w-[80vw]"
264
264
  >
@@ -419,7 +419,7 @@ watch(normalizedSearch, async () => {
419
419
  {{ t("confirm") }}
420
420
  </Button>
421
421
  </DialogFooter>
422
- </DialogContent>
422
+ </DialogScrollContent>
423
423
  </Dialog>
424
424
  </template>
425
425
 
@@ -13,10 +13,10 @@ import { Button } from "../button";
13
13
  import { Checkbox } from "../checkbox";
14
14
  import {
15
15
  Dialog,
16
- DialogContent,
17
16
  DialogDescription,
18
17
  DialogFooter,
19
18
  DialogHeader,
19
+ DialogScrollContent,
20
20
  DialogTitle
21
21
  } from "../dialog";
22
22
  import { Input } from "../input";
@@ -1625,7 +1625,7 @@ function confirmChanges() {
1625
1625
  :open="open"
1626
1626
  @update:open="handleOpenChange"
1627
1627
  >
1628
- <DialogContent
1628
+ <DialogScrollContent
1629
1629
  class="flex h-[min(42rem,calc(100vh-4rem))] w-[calc(100%-2rem)] max-h-[calc(100vh-4rem)] max-w-[calc(100%-2rem)] flex-col overflow-hidden p-0 sm:w-[80vw] sm:max-w-[80vw]"
1630
1630
  :show-close-button="true"
1631
1631
  @pointer-down-outside="(event) => event.preventDefault()"
@@ -2437,7 +2437,7 @@ function confirmChanges() {
2437
2437
  </Button>
2438
2438
  </div>
2439
2439
  </DialogFooter>
2440
- </DialogContent>
2440
+ </DialogScrollContent>
2441
2441
  </Dialog>
2442
2442
  </template>
2443
2443
 
@@ -2,6 +2,7 @@ import { defineNuxtPlugin, useNuxtApp, useRuntimeConfig } from "#app";
2
2
  import { useNavigatorLanguage } from "@vueuse/core";
3
3
  import { Effect } from "effect";
4
4
  import z from "zod";
5
+ import { setFileHandler } from "../cel/http-request.js";
5
6
  let isRedirectingAfterTokenExpiry = false;
6
7
  const processedResponses = /* @__PURE__ */ new WeakSet();
7
8
  export default defineNuxtPlugin({
@@ -121,6 +122,21 @@ export default defineNuxtPlugin({
121
122
  data
122
123
  });
123
124
  }
125
+ setFileHandler(async (request) => {
126
+ const headers = new Headers(request.headers);
127
+ applyRequestHeaders(headers);
128
+ const response = await fetch(createRequestUrl(request.url, request.query), {
129
+ method: request.method,
130
+ headers,
131
+ body: createRequestBody(request.body, headers)
132
+ });
133
+ await handleExpiredFetchResponse(response);
134
+ if (!response.ok) throw new Error(`Download failed: ${response.status}`);
135
+ const blob = await response.blob();
136
+ const disposition = response.headers.get("Content-Disposition");
137
+ const filename = disposition?.split(/filename=/i)[1]?.replaceAll(/"|'/g, "") ?? "download";
138
+ return new File([blob], decodeURIComponent(filename), { type: blob.type });
139
+ });
124
140
  const api = $fetch.create({
125
141
  baseURL: config.api.host,
126
142
  onRequest: ({
@@ -1,4 +1,5 @@
1
1
  import { Environment, EvaluationError, Optional } from "../../vendor/cel/index.js";
2
+ import { Http, HttpRequest, getFileHandler } from "./http-request.js";
2
3
  import { startOfDay, startOfWeek, startOfYear, startOfMonth, endOfDay, endOfWeek, endOfYear, endOfMonth, addYears, addMonths, addDays, addWeeks, setDate, setMonth, setYear, formatDate, isBefore, isAfter, isEqual } from "date-fns";
3
4
  import { TZDate } from "@date-fns/tz";
4
5
  import { BigNumber } from "bignumber.js";
@@ -184,5 +185,10 @@ export function createEnvironment(f = identity) {
184
185
  return Optional.none();
185
186
  }
186
187
  });
188
+ env.registerType("Http", Http).registerType("HttpRequest", HttpRequest).registerType("File", File).registerConstant("http", "Http", new Http()).registerFunction("Http.get(string): HttpRequest", (_, url) => new HttpRequest(url, "GET")).registerFunction("Http.post(string): HttpRequest", (_, url) => new HttpRequest(url, "POST")).registerFunction("Http.put(string): HttpRequest", (_, url) => new HttpRequest(url, "PUT")).registerFunction("Http.delete(string): HttpRequest", (_, url) => new HttpRequest(url, "DELETE")).registerFunction("HttpRequest.header(string, string): HttpRequest", (r, k, v) => r.withHeader(k, v)).registerFunction("HttpRequest.query(string, string): HttpRequest", (r, k, v) => r.withQuery(k, v)).registerFunction("HttpRequest.body(dyn): HttpRequest", (r, body) => r.withBody(body)).registerFunction("HttpRequest.file(): File", async (request) => {
189
+ const handler = getFileHandler();
190
+ if (!handler) throw new EvaluationError("HttpRequest.file() handler not initialized");
191
+ return handler(request);
192
+ });
187
193
  return f(env);
188
194
  }
@@ -0,0 +1,16 @@
1
+ export declare class Http {
2
+ readonly kind: "Http";
3
+ }
4
+ export declare class HttpRequest {
5
+ readonly url: string;
6
+ readonly method: string;
7
+ readonly headers: Record<string, string>;
8
+ readonly query: Record<string, string>;
9
+ readonly body: unknown;
10
+ constructor(url: string, method?: string, headers?: Record<string, string>, query?: Record<string, string>, body?: unknown);
11
+ withHeader(key: string, value: string): HttpRequest;
12
+ withQuery(key: string, value: string): HttpRequest;
13
+ withBody(body: unknown): HttpRequest;
14
+ }
15
+ export declare function getFileHandler(): ((request: HttpRequest) => Promise<File>) | null;
16
+ export declare function setFileHandler(fn: (request: HttpRequest) => Promise<File>): void;
@@ -0,0 +1,36 @@
1
+ export class Http {
2
+ kind = "Http";
3
+ }
4
+ export class HttpRequest {
5
+ url;
6
+ method;
7
+ headers;
8
+ query;
9
+ body;
10
+ constructor(url, method = "GET", headers = {}, query = {}, body = void 0) {
11
+ this.url = url;
12
+ this.method = method;
13
+ this.headers = headers;
14
+ this.query = query;
15
+ this.body = body;
16
+ Object.freeze(this);
17
+ }
18
+ withHeader(key, value) {
19
+ return new HttpRequest(this.url, this.method, { ...this.headers, [key]: value }, this.query, this.body);
20
+ }
21
+ withQuery(key, value) {
22
+ return new HttpRequest(this.url, this.method, this.headers, { ...this.query, [key]: value }, this.body);
23
+ }
24
+ withBody(body) {
25
+ return new HttpRequest(this.url, this.method, this.headers, this.query, body);
26
+ }
27
+ }
28
+ const httpState = {
29
+ fileHandler: null
30
+ };
31
+ export function getFileHandler() {
32
+ return httpState.fileHandler;
33
+ }
34
+ export function setFileHandler(fn) {
35
+ httpState.fileHandler = fn;
36
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shwfed/nuxt",
3
- "version": "0.11.35",
3
+ "version": "0.11.37",
4
4
  "description": "",
5
5
  "license": "MIT",
6
6
  "type": "module",