@atscript/vue-wf 0.1.62 → 0.1.64
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/as-wf-finish-B7mz8kVT.mjs +206 -0
- package/dist/as-wf-finish.d.mts +2 -0
- package/dist/as-wf-finish.mjs +2 -0
- package/dist/as-wf-finish.vue-BrMzuLaH.d.mts +64 -0
- package/dist/{as-wf-form-CBBvLmgY.mjs → as-wf-form-CxIEWOyU.mjs} +67 -7
- package/dist/as-wf-form.d.mts +1 -1
- package/dist/as-wf-form.mjs +2 -1
- package/dist/{as-wf-form.vue-BP739hjJ.d.mts → as-wf-form.vue-CBujwX55.d.mts} +58 -14
- package/dist/index-KRfH1NOi.d.mts +44 -0
- package/dist/index.d.mts +4 -2
- package/dist/index.mjs +3 -2
- package/package.json +13 -9
package/README.md
CHANGED
|
@@ -17,6 +17,7 @@ Part of the [atscript-ui](https://github.com/moostjs/atscript-ui) monorepo.
|
|
|
17
17
|
- `<AsWfForm>` — single component that drives the full workflow: posts current state, renders the next step's form, validates with the schema returned by the server, and resumes after pauses
|
|
18
18
|
- Hooks for custom transport, error display, and per-step UI overrides
|
|
19
19
|
- Built on [`@atscript/vue-form`](../vue-form), so all form-rendering primitives (field types, default renderers, layout grid) carry over
|
|
20
|
+
- Default `AsWfFinish` screen + `wf.finish.*` scoped slots + `navigate` prop + `@dismiss` / `@action` events for the unified `WfFinished` terminal envelope. See [Finish Screens](https://ui.atscript.dev/workflows/finish-screens).
|
|
20
21
|
|
|
21
22
|
## Install
|
|
22
23
|
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { Fragment, computed, createCommentVNode, createElementBlock, createElementVNode, defineComponent, normalizeStyle, onMounted, onUnmounted, openBlock, ref, renderList, renderSlot, toDisplayString, watch } from "vue";
|
|
2
|
+
//#region src/components/defaults/as-wf-finish.vue?vue&type=script&setup=true&lang.ts
|
|
3
|
+
const _hoisted_1 = {
|
|
4
|
+
key: 0,
|
|
5
|
+
class: "sr-only",
|
|
6
|
+
"aria-live": "polite"
|
|
7
|
+
};
|
|
8
|
+
const _hoisted_2 = ["data-level"];
|
|
9
|
+
const _hoisted_3 = {
|
|
10
|
+
key: 0,
|
|
11
|
+
class: "as-wf-finish-actions"
|
|
12
|
+
};
|
|
13
|
+
const _hoisted_4 = { class: "as-wf-finish-skip-label" };
|
|
14
|
+
const _hoisted_5 = {
|
|
15
|
+
class: "as-wf-finish-countdown",
|
|
16
|
+
"aria-live": "polite"
|
|
17
|
+
};
|
|
18
|
+
const _hoisted_6 = {
|
|
19
|
+
key: 2,
|
|
20
|
+
class: "as-wf-finish-actions"
|
|
21
|
+
};
|
|
22
|
+
const _hoisted_7 = ["autofocus", "onClick"];
|
|
23
|
+
//#endregion
|
|
24
|
+
//#region src/components/defaults/as-wf-finish.vue
|
|
25
|
+
var as_wf_finish_default = /* @__PURE__ */ defineComponent({
|
|
26
|
+
__name: "as-wf-finish",
|
|
27
|
+
props: {
|
|
28
|
+
payload: {
|
|
29
|
+
type: [Object, null],
|
|
30
|
+
required: true
|
|
31
|
+
},
|
|
32
|
+
navigate: {
|
|
33
|
+
type: Function,
|
|
34
|
+
required: false
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
emits: ["dismiss", "action"],
|
|
38
|
+
setup(__props, { emit: __emit }) {
|
|
39
|
+
const props = __props;
|
|
40
|
+
const emit = __emit;
|
|
41
|
+
async function dispatchRedirect(url) {
|
|
42
|
+
if (props.navigate) {
|
|
43
|
+
await props.navigate(url);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const loc = globalThis.location;
|
|
47
|
+
if (loc?.assign) {
|
|
48
|
+
loc.assign(url);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
console.error(`[AsWfFinish] Cannot redirect to "${url}": no \`navigate\` prop and no browser environment. Pass a \`navigate\` prop to AsWfForm / AsWfFinish.`);
|
|
52
|
+
}
|
|
53
|
+
function runAction(action) {
|
|
54
|
+
emit("action", action);
|
|
55
|
+
if (action.type === "redirect") {
|
|
56
|
+
dispatchRedirect(action.target);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
if (action.type === "reload") {
|
|
60
|
+
window.location.reload();
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if (action.type === "dismiss") emit("dismiss");
|
|
64
|
+
}
|
|
65
|
+
const end = computed(() => props.payload?.end ?? null);
|
|
66
|
+
const message = computed(() => props.payload?.message ?? null);
|
|
67
|
+
let autoTimer;
|
|
68
|
+
let countdownInterval;
|
|
69
|
+
let autoStartedAt = 0;
|
|
70
|
+
const totalSeconds = ref(0);
|
|
71
|
+
const secondsRemaining = ref(0);
|
|
72
|
+
const autoCancelled = ref(false);
|
|
73
|
+
function clearAutoTimers() {
|
|
74
|
+
if (autoTimer !== void 0) {
|
|
75
|
+
clearTimeout(autoTimer);
|
|
76
|
+
autoTimer = void 0;
|
|
77
|
+
}
|
|
78
|
+
if (countdownInterval !== void 0) {
|
|
79
|
+
clearInterval(countdownInterval);
|
|
80
|
+
countdownInterval = void 0;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
function startAutoTimer() {
|
|
84
|
+
const e = end.value;
|
|
85
|
+
if (!e || e.mode !== "auto") return;
|
|
86
|
+
clearAutoTimers();
|
|
87
|
+
autoCancelled.value = false;
|
|
88
|
+
autoStartedAt = Date.now();
|
|
89
|
+
totalSeconds.value = Math.ceil(e.timeoutMs / 1e3);
|
|
90
|
+
secondsRemaining.value = totalSeconds.value;
|
|
91
|
+
autoTimer = setTimeout(() => {
|
|
92
|
+
clearAutoTimers();
|
|
93
|
+
runAction(e.action);
|
|
94
|
+
}, e.timeoutMs);
|
|
95
|
+
countdownInterval = setInterval(() => {
|
|
96
|
+
const elapsed = Date.now() - autoStartedAt;
|
|
97
|
+
const remainMs = Math.max(0, e.timeoutMs - elapsed);
|
|
98
|
+
const next = Math.ceil(remainMs / 1e3);
|
|
99
|
+
if (next !== secondsRemaining.value) secondsRemaining.value = next;
|
|
100
|
+
}, 250);
|
|
101
|
+
}
|
|
102
|
+
function skipAuto() {
|
|
103
|
+
const e = end.value;
|
|
104
|
+
if (!e || e.mode !== "auto") return;
|
|
105
|
+
const behavior = e.skipButton?.behavior ?? "now";
|
|
106
|
+
clearAutoTimers();
|
|
107
|
+
if (behavior === "now") runAction(e.action);
|
|
108
|
+
else autoCancelled.value = true;
|
|
109
|
+
}
|
|
110
|
+
function cancelAuto() {
|
|
111
|
+
clearAutoTimers();
|
|
112
|
+
autoCancelled.value = true;
|
|
113
|
+
}
|
|
114
|
+
function applyMode() {
|
|
115
|
+
const e = end.value;
|
|
116
|
+
if (!e) return;
|
|
117
|
+
if (e.mode === "immediate") {
|
|
118
|
+
runAction(e.action);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
if (e.mode === "auto") startAutoTimer();
|
|
122
|
+
}
|
|
123
|
+
onMounted(() => applyMode());
|
|
124
|
+
watch(() => props.payload, (next, prev) => {
|
|
125
|
+
if (next === prev) return;
|
|
126
|
+
clearAutoTimers();
|
|
127
|
+
applyMode();
|
|
128
|
+
});
|
|
129
|
+
onUnmounted(() => clearAutoTimers());
|
|
130
|
+
const manualPrimary = computed(() => end.value?.mode === "manual" ? end.value.primary ?? null : null);
|
|
131
|
+
const manualOptions = computed(() => end.value?.mode === "manual" ? end.value.options ?? [] : []);
|
|
132
|
+
function onKeydown(ev) {
|
|
133
|
+
if (ev.key !== "Enter") return;
|
|
134
|
+
const target = manualPrimary.value ?? manualOptions.value[0] ?? null;
|
|
135
|
+
if (!target) return;
|
|
136
|
+
ev.preventDefault();
|
|
137
|
+
runAction(target.action);
|
|
138
|
+
}
|
|
139
|
+
function triggerButton(btn) {
|
|
140
|
+
runAction(btn.action);
|
|
141
|
+
}
|
|
142
|
+
const skipScope = computed(() => {
|
|
143
|
+
const e = end.value;
|
|
144
|
+
if (e?.mode !== "auto" || !e.skipButton) return null;
|
|
145
|
+
const behavior = e.skipButton.behavior ?? "now";
|
|
146
|
+
return {
|
|
147
|
+
label: e.skipButton.label,
|
|
148
|
+
behavior
|
|
149
|
+
};
|
|
150
|
+
});
|
|
151
|
+
return (_ctx, _cache) => {
|
|
152
|
+
return end.value?.mode === "immediate" ? (openBlock(), createElementBlock("span", _hoisted_1, "Redirecting…")) : (openBlock(), createElementBlock("div", {
|
|
153
|
+
key: 1,
|
|
154
|
+
class: "as-wf-finish",
|
|
155
|
+
onKeydown
|
|
156
|
+
}, [
|
|
157
|
+
message.value ? renderSlot(_ctx.$slots, "message", {
|
|
158
|
+
key: 0,
|
|
159
|
+
message: message.value
|
|
160
|
+
}, () => [createElementVNode("div", {
|
|
161
|
+
class: "as-wf-finish-message",
|
|
162
|
+
"data-level": message.value.level,
|
|
163
|
+
role: "status"
|
|
164
|
+
}, toDisplayString(message.value.text), 9, _hoisted_2)]) : createCommentVNode("v-if", true),
|
|
165
|
+
end.value?.mode === "auto" && !autoCancelled.value ? (openBlock(), createElementBlock(Fragment, { key: 1 }, [skipScope.value ? (openBlock(), createElementBlock("div", _hoisted_3, [renderSlot(_ctx.$slots, "skip", {
|
|
166
|
+
button: skipScope.value,
|
|
167
|
+
trigger: skipAuto
|
|
168
|
+
}, () => [createElementVNode("button", {
|
|
169
|
+
type: "button",
|
|
170
|
+
class: "as-wf-finish-skip",
|
|
171
|
+
style: normalizeStyle({ "--progress-duration": `${end.value.timeoutMs}ms` }),
|
|
172
|
+
onClick: skipAuto
|
|
173
|
+
}, [_cache[1] || (_cache[1] = createElementVNode("span", { class: "as-wf-finish-skip-fill" }, null, -1)), createElementVNode("span", _hoisted_4, toDisplayString(skipScope.value.label), 1)], 4)])])) : createCommentVNode("v-if", true), renderSlot(_ctx.$slots, "countdown", {
|
|
174
|
+
secondsRemaining: secondsRemaining.value,
|
|
175
|
+
totalSeconds: totalSeconds.value,
|
|
176
|
+
skip: skipAuto,
|
|
177
|
+
cancel: cancelAuto
|
|
178
|
+
}, () => [createElementVNode("div", _hoisted_5, " Continuing in " + toDisplayString(secondsRemaining.value) + "… ", 1)])], 64)) : createCommentVNode("v-if", true),
|
|
179
|
+
end.value?.mode === "manual" ? (openBlock(), createElementBlock("div", _hoisted_6, [manualPrimary.value ? renderSlot(_ctx.$slots, "primary", {
|
|
180
|
+
key: 0,
|
|
181
|
+
button: manualPrimary.value,
|
|
182
|
+
trigger: () => triggerButton(manualPrimary.value)
|
|
183
|
+
}, () => [createElementVNode("button", {
|
|
184
|
+
type: "button",
|
|
185
|
+
class: "as-wf-finish-primary",
|
|
186
|
+
autofocus: "",
|
|
187
|
+
onClick: _cache[0] || (_cache[0] = ($event) => triggerButton(manualPrimary.value))
|
|
188
|
+
}, toDisplayString(manualPrimary.value.label), 1)]) : createCommentVNode("v-if", true), (openBlock(true), createElementBlock(Fragment, null, renderList(manualOptions.value, (btn, index) => {
|
|
189
|
+
return renderSlot(_ctx.$slots, "option", {
|
|
190
|
+
key: index,
|
|
191
|
+
button: btn,
|
|
192
|
+
index,
|
|
193
|
+
trigger: () => triggerButton(btn)
|
|
194
|
+
}, () => [createElementVNode("button", {
|
|
195
|
+
type: "button",
|
|
196
|
+
class: "as-wf-finish-option",
|
|
197
|
+
autofocus: !manualPrimary.value && index === 0,
|
|
198
|
+
onClick: ($event) => triggerButton(btn)
|
|
199
|
+
}, toDisplayString(btn.label), 9, _hoisted_7)]);
|
|
200
|
+
}), 128))])) : createCommentVNode("v-if", true)
|
|
201
|
+
], 32));
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
//#endregion
|
|
206
|
+
export { as_wf_finish_default as t };
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { a as WfMessage, n as WfButton, r as WfFinished, t as WfAction } from "./index-KRfH1NOi.mjs";
|
|
2
|
+
import * as vue from "vue";
|
|
3
|
+
|
|
4
|
+
//#region src/components/defaults/as-wf-finish.vue.d.ts
|
|
5
|
+
type __VLS_Props = {
|
|
6
|
+
payload: WfFinished | null;
|
|
7
|
+
/**
|
|
8
|
+
* Consumer-provided navigation handler. Receives the redirect target URL —
|
|
9
|
+
* the consumer decides cross-origin vs in-app routing. Mirrors the
|
|
10
|
+
* `navigate` option on `@atscript/db-client`'s `Client`: one handler
|
|
11
|
+
* across the stack.
|
|
12
|
+
*/
|
|
13
|
+
navigate?: (url: string) => void | Promise<void>;
|
|
14
|
+
};
|
|
15
|
+
declare function skipAuto(): void;
|
|
16
|
+
declare function cancelAuto(): void;
|
|
17
|
+
declare var __VLS_1: {
|
|
18
|
+
message: WfMessage;
|
|
19
|
+
}, __VLS_3: {
|
|
20
|
+
button: {
|
|
21
|
+
readonly label: string;
|
|
22
|
+
readonly behavior: "now" | "cancel";
|
|
23
|
+
};
|
|
24
|
+
trigger: typeof skipAuto;
|
|
25
|
+
}, __VLS_5: {
|
|
26
|
+
secondsRemaining: number;
|
|
27
|
+
totalSeconds: number;
|
|
28
|
+
skip: typeof skipAuto;
|
|
29
|
+
cancel: typeof cancelAuto;
|
|
30
|
+
}, __VLS_7: {
|
|
31
|
+
button: WfButton;
|
|
32
|
+
trigger: () => void;
|
|
33
|
+
}, __VLS_9: {
|
|
34
|
+
button: WfButton;
|
|
35
|
+
index: number;
|
|
36
|
+
trigger: () => void;
|
|
37
|
+
};
|
|
38
|
+
type __VLS_Slots = {} & {
|
|
39
|
+
message?: (props: typeof __VLS_1) => any;
|
|
40
|
+
} & {
|
|
41
|
+
skip?: (props: typeof __VLS_3) => any;
|
|
42
|
+
} & {
|
|
43
|
+
countdown?: (props: typeof __VLS_5) => any;
|
|
44
|
+
} & {
|
|
45
|
+
primary?: (props: typeof __VLS_7) => any;
|
|
46
|
+
} & {
|
|
47
|
+
option?: (props: typeof __VLS_9) => any;
|
|
48
|
+
};
|
|
49
|
+
declare const __VLS_base: vue.DefineComponent<__VLS_Props, {}, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {} & {
|
|
50
|
+
dismiss: () => any;
|
|
51
|
+
action: (action: WfAction) => any;
|
|
52
|
+
}, string, vue.PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
53
|
+
onDismiss?: (() => any) | undefined;
|
|
54
|
+
onAction?: ((action: WfAction) => any) | undefined;
|
|
55
|
+
}>, {}, {}, {}, {}, string, vue.ComponentProvideOptions, false, {}, any>;
|
|
56
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
57
|
+
declare const _default: typeof __VLS_export;
|
|
58
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
59
|
+
new (): {
|
|
60
|
+
$slots: S;
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
//#endregion
|
|
64
|
+
export { _default as t };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { t as as_wf_finish_default } from "./as-wf-finish-B7mz8kVT.mjs";
|
|
2
|
+
import { computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createSlots, createVNode, defineComponent, guardReactiveProps, normalizeProps, onMounted, onUnmounted, openBlock, reactive, ref, renderSlot, shallowRef, toDisplayString, toRaw, unref, watch, withCtx } from "vue";
|
|
2
3
|
import { WF_ACTION_WITH_DATA, createFormData, createFormDef, getFieldMeta } from "@atscript/ui";
|
|
3
4
|
import { deserializeAnnotatedType } from "@atscript/typescript/utils";
|
|
4
5
|
import { AsForm } from "@atscript/vue-form";
|
|
@@ -12,6 +13,7 @@ function useWfForm(options) {
|
|
|
12
13
|
const formContext = shallowRef({});
|
|
13
14
|
const errors = shallowRef({});
|
|
14
15
|
const response = shallowRef(null);
|
|
16
|
+
const finishedPayload = shallowRef(null);
|
|
15
17
|
const error = shallowRef(null);
|
|
16
18
|
const loading = ref(false);
|
|
17
19
|
const finished = ref(false);
|
|
@@ -48,6 +50,7 @@ function useWfForm(options) {
|
|
|
48
50
|
if (data.finished) {
|
|
49
51
|
finished.value = true;
|
|
50
52
|
response.value = data;
|
|
53
|
+
finishedPayload.value = data;
|
|
51
54
|
formDef.value = null;
|
|
52
55
|
formData.value = null;
|
|
53
56
|
lastPayloadJson = void 0;
|
|
@@ -56,6 +59,7 @@ function useWfForm(options) {
|
|
|
56
59
|
if (data.sent === true || typeof data.outlet === "string") {
|
|
57
60
|
finished.value = true;
|
|
58
61
|
response.value = data;
|
|
62
|
+
finishedPayload.value = null;
|
|
59
63
|
formDef.value = null;
|
|
60
64
|
formData.value = null;
|
|
61
65
|
lastPayloadJson = void 0;
|
|
@@ -97,14 +101,20 @@ function useWfForm(options) {
|
|
|
97
101
|
signal,
|
|
98
102
|
body: JSON.stringify(buildBody(body))
|
|
99
103
|
});
|
|
104
|
+
const data = await res.json().catch(() => null);
|
|
105
|
+
if (data && typeof data === "object" && !Array.isArray(data)) extractToken(data);
|
|
100
106
|
if (!res.ok) {
|
|
101
107
|
error.value = {
|
|
102
|
-
message:
|
|
108
|
+
message: data?.message ?? res.statusText,
|
|
103
109
|
status: res.status
|
|
104
110
|
};
|
|
105
111
|
return;
|
|
106
112
|
}
|
|
107
|
-
|
|
113
|
+
if (!data) {
|
|
114
|
+
error.value = { message: "Unexpected response format" };
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
processResponse(data);
|
|
108
118
|
} catch (err) {
|
|
109
119
|
if (err instanceof DOMException && err.name === "AbortError") return;
|
|
110
120
|
error.value = { message: err instanceof Error ? err.message : "Network error" };
|
|
@@ -115,6 +125,9 @@ function useWfForm(options) {
|
|
|
115
125
|
async function start(input) {
|
|
116
126
|
const initialToken = readInitialToken();
|
|
117
127
|
if (initialToken) token = initialToken;
|
|
128
|
+
if (import.meta.env?.DEV && !initialToken && transport !== "query") {
|
|
129
|
+
if (new URLSearchParams(window.location.search).get(tokenName)) console.warn(`[useWfForm] URL contains ?${tokenName}=… but no \`initialToken\` was passed and \`tokenTransport\` is "${transport}". Workflow will start fresh instead of resuming the magic-link flow. Pass \`initial-token="route.query.${tokenName}"\` or set \`tokenTransport: "query"\`.`);
|
|
130
|
+
}
|
|
118
131
|
const body = { [wfidName]: options.name };
|
|
119
132
|
if (input) body.input = input;
|
|
120
133
|
if (initialToken) body[tokenName] = initialToken;
|
|
@@ -146,6 +159,7 @@ function useWfForm(options) {
|
|
|
146
159
|
loading,
|
|
147
160
|
finished,
|
|
148
161
|
response,
|
|
162
|
+
finishedPayload,
|
|
149
163
|
error,
|
|
150
164
|
start,
|
|
151
165
|
submit,
|
|
@@ -156,7 +170,10 @@ function useWfForm(options) {
|
|
|
156
170
|
}
|
|
157
171
|
//#endregion
|
|
158
172
|
//#region src/components/as-wf-form.vue?vue&type=script&setup=true&lang.ts
|
|
159
|
-
const _hoisted_1 = {
|
|
173
|
+
const _hoisted_1 = {
|
|
174
|
+
key: 0,
|
|
175
|
+
class: "as-wf-form-loading"
|
|
176
|
+
};
|
|
160
177
|
const _hoisted_2 = { key: 1 };
|
|
161
178
|
const _hoisted_3 = { key: 2 };
|
|
162
179
|
const _hoisted_4 = { key: 3 };
|
|
@@ -186,6 +203,10 @@ var as_wf_form_default = /* @__PURE__ */ defineComponent({
|
|
|
186
203
|
type: Function,
|
|
187
204
|
required: false
|
|
188
205
|
},
|
|
206
|
+
navigate: {
|
|
207
|
+
type: Function,
|
|
208
|
+
required: false
|
|
209
|
+
},
|
|
189
210
|
path: {
|
|
190
211
|
type: String,
|
|
191
212
|
required: true
|
|
@@ -236,7 +257,9 @@ var as_wf_form_default = /* @__PURE__ */ defineComponent({
|
|
|
236
257
|
"error",
|
|
237
258
|
"form",
|
|
238
259
|
"submit",
|
|
239
|
-
"loading"
|
|
260
|
+
"loading",
|
|
261
|
+
"dismiss",
|
|
262
|
+
"action"
|
|
240
263
|
],
|
|
241
264
|
setup(__props, { emit: __emit }) {
|
|
242
265
|
const props = __props;
|
|
@@ -289,10 +312,47 @@ var as_wf_form_default = /* @__PURE__ */ defineComponent({
|
|
|
289
312
|
submit: onSubmit,
|
|
290
313
|
retry: unref(wf).retry
|
|
291
314
|
}
|
|
292
|
-
}, () => [unref(wf).loading.value && !unref(wf).formDef.value ? (openBlock(), createElementBlock("div", _hoisted_1, [renderSlot(_ctx.$slots, "wf.loading"
|
|
315
|
+
}, () => [unref(wf).loading.value && !unref(wf).formDef.value ? (openBlock(), createElementBlock("div", _hoisted_1, [renderSlot(_ctx.$slots, "wf.loading", {}, () => [_cache[2] || (_cache[2] = createElementVNode("div", { class: "as-form-overlay" }, [createElementVNode("span", {
|
|
316
|
+
class: "as-form-overlay-icon",
|
|
317
|
+
"aria-hidden": "true"
|
|
318
|
+
})], -1))])])) : unref(wf).error.value && !unref(wf).formDef.value ? (openBlock(), createElementBlock("div", _hoisted_2, [renderSlot(_ctx.$slots, "wf.error", {
|
|
293
319
|
error: unref(wf).error.value,
|
|
294
320
|
retry: unref(wf).retry
|
|
295
|
-
}, () => [createElementVNode("div", null, toDisplayString(unref(wf).error.value?.message ?? "Error"), 1)])])) : unref(wf).finished.value ? (openBlock(), createElementBlock("div", _hoisted_3, [renderSlot(_ctx.$slots, "wf.finished", {
|
|
321
|
+
}, () => [createElementVNode("div", null, toDisplayString(unref(wf).error.value?.message ?? "Error"), 1)])])) : unref(wf).finished.value ? (openBlock(), createElementBlock("div", _hoisted_3, [renderSlot(_ctx.$slots, "wf.finished", {
|
|
322
|
+
response: unref(wf).response.value,
|
|
323
|
+
payload: unref(wf).finishedPayload.value
|
|
324
|
+
}, () => [createCommentVNode("\n Per `feedback_vue_empty_slot`: a `<template #x>` registered on a child\n short-circuits the child's default fallback even when its content is\n empty. So we only forward slot names that the AsWfForm consumer\n actually provided — otherwise AsWfFinish's defaults render.\n "), createVNode(as_wf_finish_default, {
|
|
325
|
+
payload: unref(wf).finishedPayload.value,
|
|
326
|
+
navigate: __props.navigate,
|
|
327
|
+
onDismiss: _cache[0] || (_cache[0] = () => emit("dismiss")),
|
|
328
|
+
onAction: _cache[1] || (_cache[1] = (a) => emit("action", a))
|
|
329
|
+
}, createSlots({ _: 2 }, [
|
|
330
|
+
_ctx.$slots["wf.finish.message"] ? {
|
|
331
|
+
name: "message",
|
|
332
|
+
fn: withCtx((scope) => [renderSlot(_ctx.$slots, "wf.finish.message", normalizeProps(guardReactiveProps(scope)))]),
|
|
333
|
+
key: "0"
|
|
334
|
+
} : void 0,
|
|
335
|
+
_ctx.$slots["wf.finish.countdown"] ? {
|
|
336
|
+
name: "countdown",
|
|
337
|
+
fn: withCtx((scope) => [renderSlot(_ctx.$slots, "wf.finish.countdown", normalizeProps(guardReactiveProps(scope)))]),
|
|
338
|
+
key: "1"
|
|
339
|
+
} : void 0,
|
|
340
|
+
_ctx.$slots["wf.finish.skip"] ? {
|
|
341
|
+
name: "skip",
|
|
342
|
+
fn: withCtx((scope) => [renderSlot(_ctx.$slots, "wf.finish.skip", normalizeProps(guardReactiveProps(scope)))]),
|
|
343
|
+
key: "2"
|
|
344
|
+
} : void 0,
|
|
345
|
+
_ctx.$slots["wf.finish.primary"] ? {
|
|
346
|
+
name: "primary",
|
|
347
|
+
fn: withCtx((scope) => [renderSlot(_ctx.$slots, "wf.finish.primary", normalizeProps(guardReactiveProps(scope)))]),
|
|
348
|
+
key: "3"
|
|
349
|
+
} : void 0,
|
|
350
|
+
_ctx.$slots["wf.finish.option"] ? {
|
|
351
|
+
name: "option",
|
|
352
|
+
fn: withCtx((scope) => [renderSlot(_ctx.$slots, "wf.finish.option", normalizeProps(guardReactiveProps(scope)))]),
|
|
353
|
+
key: "4"
|
|
354
|
+
} : void 0
|
|
355
|
+
]), 1032, ["payload", "navigate"])])])) : unref(wf).formDef.value && unref(wf).formData.value ? (openBlock(), createElementBlock("div", _hoisted_4, [unref(wf).error.value ? renderSlot(_ctx.$slots, "form.error", {
|
|
296
356
|
key: 0,
|
|
297
357
|
error: unref(wf).error.value,
|
|
298
358
|
retry: unref(wf).retry
|
package/dist/as-wf-form.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as _default } from "./as-wf-form.vue-
|
|
1
|
+
import { t as _default } from "./as-wf-form.vue-CBujwX55.mjs";
|
|
2
2
|
export { _default as default };
|
package/dist/as-wf-form.mjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { a as WfMessage, n as WfButton, r as WfFinished, t as WfAction } from "./index-KRfH1NOi.mjs";
|
|
1
2
|
import * as vue from "vue";
|
|
2
3
|
import { Component, Ref, ShallowRef } from "vue";
|
|
3
4
|
import { ClientFactory, FormDef } from "@atscript/ui";
|
|
@@ -51,6 +52,8 @@ interface UseWfFormReturn {
|
|
|
51
52
|
loading: Ref<boolean>;
|
|
52
53
|
finished: Ref<boolean>;
|
|
53
54
|
response: ShallowRef<unknown>;
|
|
55
|
+
/** Typed envelope on finish — null while running, on outlet pause, or before first response. */
|
|
56
|
+
finishedPayload: ShallowRef<WfFinished | null>;
|
|
54
57
|
error: ShallowRef<unknown>;
|
|
55
58
|
start: (input?: Record<string, unknown>) => Promise<void>;
|
|
56
59
|
submit: (data: unknown) => Promise<void>;
|
|
@@ -70,6 +73,12 @@ interface AsWfFormProps extends UseWfFormOptions {
|
|
|
70
73
|
components?: Record<string, Component>;
|
|
71
74
|
/** Per-form client factory override (FK value-help). Forwarded to AsForm. */
|
|
72
75
|
clientFactory?: ClientFactory;
|
|
76
|
+
/**
|
|
77
|
+
* Consumer-provided navigation handler forwarded to `<AsWfFinish>`. Pairs
|
|
78
|
+
* with `@atscript/db-client`'s `Client({ navigate })` option so one handler
|
|
79
|
+
* covers both workflow redirects and DB navigate-actions.
|
|
80
|
+
*/
|
|
81
|
+
navigate?: (url: string) => void | Promise<void>;
|
|
73
82
|
}
|
|
74
83
|
declare function onSubmit(data: unknown): void;
|
|
75
84
|
declare var __VLS_1: {
|
|
@@ -94,31 +103,52 @@ declare var __VLS_1: {
|
|
|
94
103
|
retry: () => Promise<void>;
|
|
95
104
|
}, __VLS_7: {
|
|
96
105
|
response: unknown;
|
|
97
|
-
|
|
106
|
+
payload: WfFinished<unknown> | null;
|
|
107
|
+
}, __VLS_19: {
|
|
108
|
+
message: WfMessage;
|
|
109
|
+
}, __VLS_22: {
|
|
110
|
+
secondsRemaining: number;
|
|
111
|
+
totalSeconds: number;
|
|
112
|
+
skip: () => void;
|
|
113
|
+
cancel: () => void;
|
|
114
|
+
}, __VLS_25: {
|
|
115
|
+
button: {
|
|
116
|
+
readonly label: string;
|
|
117
|
+
readonly behavior: "now" | "cancel";
|
|
118
|
+
};
|
|
119
|
+
trigger: () => void;
|
|
120
|
+
}, __VLS_28: {
|
|
121
|
+
button: WfButton;
|
|
122
|
+
trigger: () => void;
|
|
123
|
+
}, __VLS_31: {
|
|
124
|
+
button: WfButton;
|
|
125
|
+
index: number;
|
|
126
|
+
trigger: () => void;
|
|
127
|
+
}, __VLS_33: {
|
|
98
128
|
error: {};
|
|
99
129
|
retry: () => Promise<void>;
|
|
100
|
-
},
|
|
130
|
+
}, __VLS_46: {
|
|
101
131
|
loading: boolean;
|
|
102
132
|
clearErrors: () => void;
|
|
103
133
|
reset: () => Promise<void>;
|
|
104
134
|
setErrors: (errors: Record<string, string>) => void;
|
|
105
135
|
formContext: Record<string, unknown> | undefined;
|
|
106
136
|
disabled: boolean;
|
|
107
|
-
},
|
|
137
|
+
}, __VLS_49: {
|
|
108
138
|
loading: boolean;
|
|
109
139
|
clearErrors: () => void;
|
|
110
140
|
reset: () => Promise<void>;
|
|
111
141
|
setErrors: (errors: Record<string, string>) => void;
|
|
112
142
|
formContext: Record<string, unknown> | undefined;
|
|
113
143
|
disabled: boolean;
|
|
114
|
-
},
|
|
144
|
+
}, __VLS_52: {
|
|
115
145
|
loading: boolean;
|
|
116
146
|
clearErrors: () => void;
|
|
117
147
|
reset: () => Promise<void>;
|
|
118
148
|
setErrors: (errors: Record<string, string>) => void;
|
|
119
149
|
disabled: boolean;
|
|
120
150
|
formContext: Record<string, unknown> | undefined;
|
|
121
|
-
},
|
|
151
|
+
}, __VLS_55: {
|
|
122
152
|
loading: boolean;
|
|
123
153
|
disabled: boolean;
|
|
124
154
|
text: string;
|
|
@@ -126,7 +156,7 @@ declare var __VLS_1: {
|
|
|
126
156
|
reset: () => Promise<void>;
|
|
127
157
|
setErrors: (errors: Record<string, string>) => void;
|
|
128
158
|
formContext: Record<string, unknown> | undefined;
|
|
129
|
-
},
|
|
159
|
+
}, __VLS_58: {
|
|
130
160
|
loading: boolean;
|
|
131
161
|
disabled: boolean;
|
|
132
162
|
clearErrors: () => void;
|
|
@@ -143,33 +173,47 @@ type __VLS_Slots = {} & {
|
|
|
143
173
|
} & {
|
|
144
174
|
'wf.finished'?: (props: typeof __VLS_7) => any;
|
|
145
175
|
} & {
|
|
146
|
-
'
|
|
176
|
+
'wf.finish.message'?: (props: typeof __VLS_19) => any;
|
|
177
|
+
} & {
|
|
178
|
+
'wf.finish.countdown'?: (props: typeof __VLS_22) => any;
|
|
179
|
+
} & {
|
|
180
|
+
'wf.finish.skip'?: (props: typeof __VLS_25) => any;
|
|
181
|
+
} & {
|
|
182
|
+
'wf.finish.primary'?: (props: typeof __VLS_28) => any;
|
|
183
|
+
} & {
|
|
184
|
+
'wf.finish.option'?: (props: typeof __VLS_31) => any;
|
|
147
185
|
} & {
|
|
148
|
-
'form.
|
|
186
|
+
'form.error'?: (props: typeof __VLS_33) => any;
|
|
149
187
|
} & {
|
|
150
|
-
'form.
|
|
188
|
+
'form.header'?: (props: typeof __VLS_46) => any;
|
|
151
189
|
} & {
|
|
152
|
-
'form.
|
|
190
|
+
'form.before'?: (props: typeof __VLS_49) => any;
|
|
153
191
|
} & {
|
|
154
|
-
'form.
|
|
192
|
+
'form.after'?: (props: typeof __VLS_52) => any;
|
|
155
193
|
} & {
|
|
156
|
-
'form.
|
|
194
|
+
'form.submit'?: (props: typeof __VLS_55) => any;
|
|
195
|
+
} & {
|
|
196
|
+
'form.footer'?: (props: typeof __VLS_58) => any;
|
|
157
197
|
};
|
|
158
198
|
declare const __VLS_base: vue.DefineComponent<AsWfFormProps, {}, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {} & {
|
|
159
|
-
|
|
199
|
+
dismiss: () => any;
|
|
200
|
+
action: (action: WfAction) => any;
|
|
160
201
|
error: (error: {
|
|
161
202
|
message: string;
|
|
162
203
|
status?: number;
|
|
163
204
|
}) => any;
|
|
205
|
+
finished: (response: unknown) => any;
|
|
164
206
|
submit: (data: unknown) => any;
|
|
165
207
|
loading: (isLoading: boolean) => any;
|
|
166
208
|
form: (def: FormDef, context?: Record<string, unknown> | undefined) => any;
|
|
167
209
|
}, string, vue.PublicProps, Readonly<AsWfFormProps> & Readonly<{
|
|
168
|
-
|
|
210
|
+
onDismiss?: (() => any) | undefined;
|
|
211
|
+
onAction?: ((action: WfAction) => any) | undefined;
|
|
169
212
|
onError?: ((error: {
|
|
170
213
|
message: string;
|
|
171
214
|
status?: number;
|
|
172
215
|
}) => any) | undefined;
|
|
216
|
+
onFinished?: ((response: unknown) => any) | undefined;
|
|
173
217
|
onSubmit?: ((data: unknown) => any) | undefined;
|
|
174
218
|
onLoading?: ((isLoading: boolean) => any) | undefined;
|
|
175
219
|
onForm?: ((def: FormDef, context?: Record<string, unknown> | undefined) => any) | undefined;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
//#region ../moost-wf/src/wf-finished.d.ts
|
|
2
|
+
interface WfFinished<TData = unknown> {
|
|
3
|
+
finished: true;
|
|
4
|
+
data?: TData;
|
|
5
|
+
message?: WfMessage;
|
|
6
|
+
end?: WfFinishedEnd;
|
|
7
|
+
aborted?: boolean;
|
|
8
|
+
reason?: string;
|
|
9
|
+
}
|
|
10
|
+
interface WfMessage {
|
|
11
|
+
level: "info" | "success" | "warn" | "error";
|
|
12
|
+
text: string;
|
|
13
|
+
}
|
|
14
|
+
type WfFinishedEnd = {
|
|
15
|
+
mode: "immediate";
|
|
16
|
+
action: WfAction;
|
|
17
|
+
} | {
|
|
18
|
+
mode: "auto";
|
|
19
|
+
timeoutMs: number;
|
|
20
|
+
action: WfAction;
|
|
21
|
+
skipButton?: {
|
|
22
|
+
label: string;
|
|
23
|
+
behavior?: "now" | "cancel";
|
|
24
|
+
};
|
|
25
|
+
} | {
|
|
26
|
+
mode: "manual";
|
|
27
|
+
primary?: WfButton;
|
|
28
|
+
options?: WfButton[];
|
|
29
|
+
};
|
|
30
|
+
interface WfButton {
|
|
31
|
+
label: string;
|
|
32
|
+
action: WfAction;
|
|
33
|
+
}
|
|
34
|
+
type WfAction = {
|
|
35
|
+
type: "redirect";
|
|
36
|
+
target: string;
|
|
37
|
+
reason?: string;
|
|
38
|
+
} | {
|
|
39
|
+
type: "reload";
|
|
40
|
+
} | {
|
|
41
|
+
type: "dismiss";
|
|
42
|
+
};
|
|
43
|
+
//#endregion
|
|
44
|
+
export { WfMessage as a, WfFinishedEnd as i, WfButton as n, WfFinished as r, WfAction as t };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,2 +1,4 @@
|
|
|
1
|
-
import { i as
|
|
2
|
-
|
|
1
|
+
import { a as WfMessage, i as WfFinishedEnd, n as WfButton, r as WfFinished, t as WfAction } from "./index-KRfH1NOi.mjs";
|
|
2
|
+
import { i as useWfForm, n as UseWfFormOptions, r as UseWfFormReturn, t as _default$1 } from "./as-wf-form.vue-CBujwX55.mjs";
|
|
3
|
+
import { t as _default } from "./as-wf-finish.vue-BrMzuLaH.mjs";
|
|
4
|
+
export { _default as AsWfFinish, _default$1 as AsWfForm, type UseWfFormOptions, type UseWfFormReturn, type WfAction, type WfButton, type WfFinished, type WfFinishedEnd, type WfMessage, useWfForm };
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
import { n as useWfForm, t as as_wf_form_default } from "./as-wf-form-
|
|
2
|
-
|
|
1
|
+
import { n as useWfForm, t as as_wf_form_default } from "./as-wf-form-CxIEWOyU.mjs";
|
|
2
|
+
import { t as as_wf_finish_default } from "./as-wf-finish-B7mz8kVT.mjs";
|
|
3
|
+
export { as_wf_finish_default as AsWfFinish, as_wf_form_default as AsWfForm, useWfForm };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atscript/vue-wf",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.64",
|
|
4
4
|
"description": "Workflow form integration for Vue 3 — HTTP round-trip loop driven by atscript type metadata",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"atscript",
|
|
@@ -36,6 +36,10 @@
|
|
|
36
36
|
"types": "./dist/index.d.mts",
|
|
37
37
|
"import": "./dist/index.mjs"
|
|
38
38
|
},
|
|
39
|
+
"./as-wf-finish": {
|
|
40
|
+
"types": "./dist/as-wf-finish.d.mts",
|
|
41
|
+
"import": "./dist/as-wf-finish.mjs"
|
|
42
|
+
},
|
|
39
43
|
"./as-wf-form": {
|
|
40
44
|
"types": "./dist/as-wf-form.d.mts",
|
|
41
45
|
"import": "./dist/as-wf-form.mjs"
|
|
@@ -46,25 +50,25 @@
|
|
|
46
50
|
"access": "public"
|
|
47
51
|
},
|
|
48
52
|
"dependencies": {
|
|
49
|
-
"@atscript/ui": "^0.1.
|
|
53
|
+
"@atscript/ui": "^0.1.64"
|
|
50
54
|
},
|
|
51
55
|
"devDependencies": {
|
|
52
|
-
"@atscript/core": "^0.1.
|
|
53
|
-
"@atscript/typescript": "^0.1.
|
|
56
|
+
"@atscript/core": "^0.1.56",
|
|
57
|
+
"@atscript/typescript": "^0.1.56",
|
|
54
58
|
"@vitejs/plugin-vue": "^6",
|
|
55
59
|
"@vue/test-utils": "^2",
|
|
56
60
|
"happy-dom": "^18",
|
|
57
|
-
"unplugin-atscript": "^0.1.
|
|
61
|
+
"unplugin-atscript": "^0.1.56",
|
|
58
62
|
"vitest": "npm:@voidzero-dev/vite-plus-test@latest",
|
|
59
63
|
"vue": "^3",
|
|
60
64
|
"vue-tsc": "^3.2.6",
|
|
61
|
-
"@atscript/
|
|
62
|
-
"@atscript/
|
|
65
|
+
"@atscript/moost-wf": "^0.1.64",
|
|
66
|
+
"@atscript/vue-form": "^0.1.64"
|
|
63
67
|
},
|
|
64
68
|
"peerDependencies": {
|
|
65
|
-
"@atscript/typescript": "^0.1.
|
|
69
|
+
"@atscript/typescript": "^0.1.56",
|
|
66
70
|
"vue": "^3",
|
|
67
|
-
"@atscript/vue-form": "^0.1.
|
|
71
|
+
"@atscript/vue-form": "^0.1.64"
|
|
68
72
|
},
|
|
69
73
|
"scripts": {
|
|
70
74
|
"build": "node ../../scripts/gen-exports.mjs && vp pack",
|