@effect-app/vue-components 4.0.0-beta.3 → 4.0.0-beta.300
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 +36 -8
- package/dist/reset.css +52 -0
- package/dist/types/components/CommandButton.vue.d.ts +24 -5
- package/dist/types/components/Dialog.vue.d.ts +3 -1
- package/dist/types/components/FixedNuxtErrorBoundary.vue.d.ts +39 -0
- package/dist/types/components/OmegaForm/InputProps.d.ts +1 -1
- package/dist/types/components/OmegaForm/OmegaArray.vue.d.ts +1 -1
- package/dist/types/components/OmegaForm/OmegaAutoGen.vue.d.ts +1 -1
- package/dist/types/components/OmegaForm/OmegaErrorsInternal.vue.d.ts +1 -1
- package/dist/types/components/OmegaForm/OmegaFormInput.vue.d.ts +1 -1
- package/dist/types/components/OmegaForm/OmegaInput.vue.d.ts +1 -1
- package/dist/types/components/OmegaForm/OmegaInputVuetify.vue.d.ts +6 -0
- package/dist/types/components/OmegaForm/OmegaInternalInput.vue.d.ts +6 -9
- package/dist/types/components/OmegaForm/OmegaTaggedUnion.vue.d.ts +2 -2
- package/dist/types/components/OmegaForm/OmegaTaggedUnionInternal.vue.d.ts +3 -3
- package/dist/types/components/OmegaForm/OmegaWrapper.vue.d.ts +1 -1
- package/dist/types/components/OmegaForm/createUseFormWithCustomInput.d.ts +4 -2
- package/dist/types/components/OmegaForm/errors.d.ts +33 -0
- package/dist/types/components/OmegaForm/getOmegaStore.d.ts +1 -1
- package/dist/types/components/OmegaForm/hocs.d.ts +3 -0
- package/dist/types/components/OmegaForm/index.d.ts +14 -3
- package/dist/types/components/OmegaForm/inputs.d.ts +4 -0
- package/dist/types/components/OmegaForm/meta/checks.d.ts +4 -0
- package/dist/types/components/OmegaForm/meta/createMeta.d.ts +33 -0
- package/dist/types/components/OmegaForm/meta/defaults.d.ts +21 -0
- package/dist/types/components/OmegaForm/meta/redacted.d.ts +2 -0
- package/dist/types/components/OmegaForm/meta/types.d.ts +56 -0
- package/dist/types/components/OmegaForm/meta/walker.d.ts +18 -0
- package/dist/types/components/OmegaForm/persistency.d.ts +58 -0
- package/dist/types/components/OmegaForm/submit.d.ts +60 -0
- package/dist/types/components/OmegaForm/types.d.ts +312 -0
- package/dist/types/components/OmegaForm/useOmegaForm.d.ts +9 -215
- package/dist/types/components/OmegaForm/validation/localized.d.ts +10 -0
- package/dist/types/components/index.d.ts +1 -0
- package/dist/types/index.d.ts +0 -1
- package/dist/types/utils/index.d.ts +8 -9
- package/dist/vue-components.es.js +30 -44
- package/dist/vue-components10.es.js +5 -0
- package/dist/vue-components11.es.js +56 -0
- package/dist/vue-components12.es.js +5 -0
- package/dist/vue-components13.es.js +20 -0
- package/dist/vue-components14.es.js +49 -0
- package/dist/vue-components15.es.js +128 -0
- package/dist/vue-components16.es.js +65 -0
- package/dist/vue-components17.es.js +114 -0
- package/dist/vue-components18.es.js +22 -0
- package/dist/vue-components19.es.js +5 -0
- package/dist/vue-components2.es.js +11 -0
- package/dist/vue-components20.es.js +80 -0
- package/dist/vue-components21.es.js +93 -0
- package/dist/vue-components22.es.js +73 -0
- package/dist/vue-components23.es.js +12 -0
- package/dist/vue-components24.es.js +57 -0
- package/dist/vue-components25.es.js +5 -0
- package/dist/vue-components26.es.js +44 -0
- package/dist/vue-components27.es.js +5 -0
- package/dist/vue-components28.es.js +84 -0
- package/dist/vue-components3.es.js +98 -0
- package/dist/vue-components30.es.js +8 -0
- package/dist/vue-components31.es.js +9 -0
- package/dist/vue-components32.es.js +24 -0
- package/dist/vue-components33.es.js +300 -0
- package/dist/vue-components35.es.js +8 -0
- package/dist/vue-components36.es.js +73 -0
- package/dist/vue-components37.es.js +5 -0
- package/dist/vue-components38.es.js +64 -0
- package/dist/vue-components39.es.js +5 -0
- package/dist/vue-components4.es.js +5 -0
- package/dist/vue-components40.es.js +24 -0
- package/dist/vue-components41.es.js +5 -0
- package/dist/vue-components42.es.js +59 -0
- package/dist/vue-components43.es.js +5 -0
- package/dist/vue-components44.es.js +12 -0
- package/dist/vue-components45.es.js +22 -0
- package/dist/vue-components47.es.js +9 -0
- package/dist/vue-components48.es.js +4 -0
- package/dist/vue-components49.es.js +38 -0
- package/dist/vue-components5.es.js +24 -0
- package/dist/vue-components50.es.js +27 -0
- package/dist/vue-components51.es.js +28 -0
- package/dist/vue-components52.es.js +7 -0
- package/dist/vue-components53.es.js +18 -0
- package/dist/vue-components54.es.js +36 -0
- package/dist/vue-components55.es.js +18 -0
- package/dist/vue-components56.es.js +21 -0
- package/dist/vue-components57.es.js +30 -0
- package/dist/vue-components58.es.js +7 -0
- package/dist/vue-components59.es.js +9 -0
- package/dist/vue-components6.es.js +13 -0
- package/dist/vue-components60.es.js +38 -0
- package/dist/vue-components61.es.js +25 -0
- package/dist/vue-components62.es.js +128 -0
- package/dist/vue-components63.es.js +24 -0
- package/dist/vue-components64.es.js +21 -0
- package/dist/vue-components65.es.js +9 -0
- package/dist/vue-components66.es.js +19 -0
- package/dist/vue-components67.es.js +5 -0
- package/dist/vue-components68.es.js +29 -0
- package/dist/vue-components69.es.js +5 -0
- package/dist/vue-components7.es.js +13 -0
- package/dist/vue-components70.es.js +43 -0
- package/dist/vue-components71.es.js +101 -0
- package/dist/vue-components72.es.js +36 -0
- package/dist/vue-components73.es.js +19 -0
- package/dist/vue-components74.es.js +50 -0
- package/dist/vue-components8.es.js +35 -0
- package/dist/vue-components9.es.js +47 -0
- package/package.json +44 -34
- package/src/components/CommandButton.vue +96 -16
- package/src/components/Dialog.vue +0 -1
- package/src/components/FixedNuxtErrorBoundary.vue +112 -0
- package/src/components/OmegaForm/InputProps.ts +1 -1
- package/src/components/OmegaForm/OmegaArray.vue +8 -9
- package/src/components/OmegaForm/OmegaAutoGen.vue +26 -30
- package/src/components/OmegaForm/OmegaErrorsInternal.vue +3 -4
- package/src/components/OmegaForm/OmegaFormInput.vue +1 -1
- package/src/components/OmegaForm/OmegaInput.vue +26 -40
- package/src/components/OmegaForm/OmegaInputVuetify.vue +58 -8
- package/src/components/OmegaForm/OmegaInternalInput.vue +41 -18
- package/src/components/OmegaForm/OmegaTaggedUnion.vue +10 -3
- package/src/components/OmegaForm/OmegaTaggedUnionInternal.vue +6 -6
- package/src/components/OmegaForm/OmegaWrapper.vue +1 -1
- package/src/components/OmegaForm/blockDialog.ts +18 -6
- package/src/components/OmegaForm/createUseFormWithCustomInput.ts +16 -14
- package/src/components/OmegaForm/errors.ts +136 -0
- package/src/components/OmegaForm/getOmegaStore.ts +1 -1
- package/src/components/OmegaForm/hocs.ts +19 -0
- package/src/components/OmegaForm/index.ts +17 -4
- package/src/components/OmegaForm/inputs.ts +22 -0
- package/src/components/OmegaForm/meta/checks.ts +82 -0
- package/src/components/OmegaForm/meta/createMeta.ts +140 -0
- package/src/components/OmegaForm/meta/defaults.ts +261 -0
- package/src/components/OmegaForm/meta/redacted.ts +66 -0
- package/src/components/OmegaForm/meta/types.ts +78 -0
- package/src/components/OmegaForm/meta/walker.ts +248 -0
- package/src/components/OmegaForm/persistency.ts +247 -0
- package/src/components/OmegaForm/submit.ts +131 -0
- package/src/components/OmegaForm/types.ts +801 -0
- package/src/components/OmegaForm/useOmegaForm.ts +103 -898
- package/src/components/OmegaForm/useRegisterField.ts +1 -1
- package/src/components/OmegaForm/validation/localized.ts +203 -0
- package/src/components/index.ts +1 -0
- package/src/index.ts +0 -1
- package/src/reset.css +52 -0
- package/src/utils/index.ts +10 -11
- package/dist/types/components/OmegaForm/OmegaFormStuff.d.ts +0 -157
- package/dist/types/constants/index.d.ts +0 -1
- package/dist/vue-components.es10.js +0 -237
- package/dist/vue-components.es100.js +0 -4
- package/dist/vue-components.es11.js +0 -32
- package/dist/vue-components.es12.js +0 -439
- package/dist/vue-components.es13.js +0 -49
- package/dist/vue-components.es14.js +0 -4
- package/dist/vue-components.es15.js +0 -4
- package/dist/vue-components.es16.js +0 -725
- package/dist/vue-components.es17.js +0 -143
- package/dist/vue-components.es18.js +0 -6
- package/dist/vue-components.es19.js +0 -13
- package/dist/vue-components.es2.js +0 -30
- package/dist/vue-components.es20.js +0 -5
- package/dist/vue-components.es21.js +0 -26
- package/dist/vue-components.es22.js +0 -6
- package/dist/vue-components.es23.js +0 -10
- package/dist/vue-components.es24.js +0 -57
- package/dist/vue-components.es25.js +0 -71
- package/dist/vue-components.es26.js +0 -8
- package/dist/vue-components.es27.js +0 -8
- package/dist/vue-components.es28.js +0 -5
- package/dist/vue-components.es29.js +0 -5
- package/dist/vue-components.es3.js +0 -16
- package/dist/vue-components.es30.js +0 -4
- package/dist/vue-components.es31.js +0 -4
- package/dist/vue-components.es32.js +0 -4
- package/dist/vue-components.es33.js +0 -4
- package/dist/vue-components.es34.js +0 -19
- package/dist/vue-components.es35.js +0 -13
- package/dist/vue-components.es36.js +0 -40
- package/dist/vue-components.es37.js +0 -6
- package/dist/vue-components.es38.js +0 -85
- package/dist/vue-components.es39.js +0 -54
- package/dist/vue-components.es4.js +0 -52
- package/dist/vue-components.es40.js +0 -563
- package/dist/vue-components.es41.js +0 -43
- package/dist/vue-components.es42.js +0 -29
- package/dist/vue-components.es43.js +0 -7
- package/dist/vue-components.es44.js +0 -42
- package/dist/vue-components.es45.js +0 -316
- package/dist/vue-components.es46.js +0 -33
- package/dist/vue-components.es47.js +0 -6
- package/dist/vue-components.es48.js +0 -26
- package/dist/vue-components.es49.js +0 -77
- package/dist/vue-components.es5.js +0 -52
- package/dist/vue-components.es50.js +0 -101
- package/dist/vue-components.es51.js +0 -4
- package/dist/vue-components.es52.js +0 -320
- package/dist/vue-components.es53.js +0 -66
- package/dist/vue-components.es54.js +0 -4
- package/dist/vue-components.es55.js +0 -4
- package/dist/vue-components.es56.js +0 -113
- package/dist/vue-components.es58.js +0 -9
- package/dist/vue-components.es59.js +0 -34
- package/dist/vue-components.es6.js +0 -69
- package/dist/vue-components.es61.js +0 -194
- package/dist/vue-components.es63.js +0 -6
- package/dist/vue-components.es64.js +0 -103
- package/dist/vue-components.es65.js +0 -84
- package/dist/vue-components.es66.js +0 -17
- package/dist/vue-components.es67.js +0 -34
- package/dist/vue-components.es68.js +0 -4
- package/dist/vue-components.es69.js +0 -23
- package/dist/vue-components.es7.js +0 -83
- package/dist/vue-components.es70.js +0 -14
- package/dist/vue-components.es71.js +0 -115
- package/dist/vue-components.es72.js +0 -5
- package/dist/vue-components.es73.js +0 -4
- package/dist/vue-components.es74.js +0 -4
- package/dist/vue-components.es75.js +0 -17
- package/dist/vue-components.es76.js +0 -72
- package/dist/vue-components.es77.js +0 -25
- package/dist/vue-components.es78.js +0 -7
- package/dist/vue-components.es79.js +0 -23
- package/dist/vue-components.es8.js +0 -63
- package/dist/vue-components.es80.js +0 -32
- package/dist/vue-components.es81.js +0 -24
- package/dist/vue-components.es82.js +0 -14
- package/dist/vue-components.es83.js +0 -7
- package/dist/vue-components.es84.js +0 -21
- package/dist/vue-components.es85.js +0 -11
- package/dist/vue-components.es86.js +0 -33
- package/dist/vue-components.es87.js +0 -50
- package/dist/vue-components.es88.js +0 -28
- package/dist/vue-components.es89.js +0 -18
- package/dist/vue-components.es9.js +0 -21
- package/dist/vue-components.es90.js +0 -10
- package/dist/vue-components.es91.js +0 -13
- package/dist/vue-components.es92.js +0 -67
- package/dist/vue-components.es93.js +0 -58
- package/dist/vue-components.es94.js +0 -19
- package/dist/vue-components.es95.js +0 -35
- package/dist/vue-components.es96.js +0 -31
- package/dist/vue-components.es97.js +0 -44
- package/dist/vue-components.es98.js +0 -4
- package/dist/vue-components.es99.js +0 -46
- package/src/components/OmegaForm/OmegaFormStuff.ts +0 -1174
- package/src/constants/index.ts +0 -1
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
<script
|
|
2
2
|
setup
|
|
3
3
|
lang="ts"
|
|
4
|
-
generic="I = never"
|
|
4
|
+
generic="I = never, RA = unknown, RE = unknown"
|
|
5
5
|
>
|
|
6
|
-
import type { CommandBase } from "@effect-app/vue"
|
|
6
|
+
import type { CommandBase, Progress } from "@effect-app/vue/makeClient"
|
|
7
|
+
import * as Option from "effect-app/Option"
|
|
8
|
+
import type * as AsyncResult from "effect/unstable/reactivity/AsyncResult"
|
|
7
9
|
import { computed } from "vue"
|
|
8
10
|
import type { VBtn } from "vuetify/components"
|
|
9
11
|
|
|
@@ -14,38 +16,89 @@ const props = defineProps<
|
|
|
14
16
|
& (
|
|
15
17
|
| {
|
|
16
18
|
input: NoInfer<I>
|
|
17
|
-
|
|
19
|
+
optionalInput?: undefined
|
|
20
|
+
command: CommandBase<I, any, RA, RE>
|
|
18
21
|
empty?: boolean
|
|
19
22
|
}
|
|
20
23
|
| {
|
|
21
|
-
|
|
24
|
+
optionalInput: Option.Option<NoInfer<I>>
|
|
22
25
|
input?: undefined
|
|
26
|
+
command: CommandBase<I, any, RA, RE>
|
|
27
|
+
empty?: boolean
|
|
28
|
+
}
|
|
29
|
+
| {
|
|
30
|
+
command: CommandBase<any, any, RA, RE>
|
|
31
|
+
input?: undefined
|
|
32
|
+
optionalInput?: undefined
|
|
23
33
|
empty?: boolean
|
|
24
34
|
}
|
|
25
35
|
)
|
|
26
36
|
& {
|
|
27
37
|
disabled?: ButtonProps["disabled"]
|
|
28
38
|
title?: string // why isn't it part of VBtnProps??
|
|
39
|
+
mapProgress?: (result: AsyncResult.AsyncResult<RA, RE>) => Progress | undefined
|
|
29
40
|
}
|
|
30
41
|
& ButtonProps
|
|
31
42
|
>()
|
|
32
43
|
|
|
33
|
-
const
|
|
44
|
+
const resolvedInput = computed<{ _tag: "ready"; value: I } | { _tag: "missing" } | { _tag: "void" }>(() => {
|
|
45
|
+
if ("optionalInput" in props && props.optionalInput !== undefined) {
|
|
46
|
+
return Option.isSome(props.optionalInput)
|
|
47
|
+
? { _tag: "ready", value: props.optionalInput.value }
|
|
48
|
+
: { _tag: "missing" }
|
|
49
|
+
}
|
|
50
|
+
if ("input" in props && props.input !== undefined) {
|
|
51
|
+
return { _tag: "ready", value: props.input as I }
|
|
52
|
+
}
|
|
53
|
+
return { _tag: "void" }
|
|
54
|
+
})
|
|
34
55
|
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
56
|
+
const isDisabled = computed(() =>
|
|
57
|
+
props.command.blocked
|
|
58
|
+
|| props.disabled
|
|
59
|
+
|| resolvedInput.value._tag === "missing"
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
const resolvedProgress = computed(() => {
|
|
63
|
+
if (props.mapProgress) {
|
|
64
|
+
const result = props.command.result
|
|
65
|
+
return result !== undefined ? props.mapProgress(result) : undefined
|
|
39
66
|
}
|
|
67
|
+
return props.command.progress
|
|
68
|
+
})
|
|
40
69
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
70
|
+
const progressText = computed(() => {
|
|
71
|
+
const p = resolvedProgress.value
|
|
72
|
+
if (p === undefined) return undefined
|
|
73
|
+
return typeof p === "string" ? p : p.text
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
const progressPercentage = computed(() => {
|
|
77
|
+
const p = resolvedProgress.value
|
|
78
|
+
return typeof p === "object" && p !== null ? p.percentage : undefined
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
const handleClick = () => {
|
|
82
|
+
if (isDisabled.value) return
|
|
83
|
+
const ri = resolvedInput.value
|
|
84
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
85
|
+
const handle = props.command.handle as any
|
|
86
|
+
if (ri._tag === "ready") handle(ri.value)
|
|
87
|
+
else if (ri._tag === "void") handle(undefined)
|
|
88
|
+
// _tag === "missing" can't reach here — guarded by isDisabled
|
|
45
89
|
}
|
|
46
90
|
</script>
|
|
47
91
|
<script lang="ts">
|
|
48
|
-
/**
|
|
92
|
+
/**
|
|
93
|
+
* CommandButton connects a command to a button and tracks disabled / loading state.
|
|
94
|
+
*
|
|
95
|
+
* Input variants:
|
|
96
|
+
* - `input`: pass a validated value; button enabled when command + disabled allow it.
|
|
97
|
+
* - `optionalInput`: pass an `Option<I>`. `Some` → enabled, click fires with the value.
|
|
98
|
+
* `None` → disabled. Use this when the input is gated by a `computed` instead of
|
|
99
|
+
* a `v-if` on the button itself.
|
|
100
|
+
* - neither: command takes no input.
|
|
101
|
+
*/
|
|
49
102
|
export default {
|
|
50
103
|
name: "CommandButton"
|
|
51
104
|
}
|
|
@@ -55,27 +108,54 @@ export default {
|
|
|
55
108
|
v-if="command.allowed && !empty"
|
|
56
109
|
v-bind="$attrs"
|
|
57
110
|
:loading="command.waiting"
|
|
111
|
+
:disabled="isDisabled"
|
|
58
112
|
:aria-disabled="isDisabled"
|
|
59
113
|
:title="title ?? command.action"
|
|
60
114
|
:class="{ 'v-btn--disabled': isDisabled }"
|
|
61
115
|
@click="handleClick"
|
|
62
116
|
>
|
|
117
|
+
<template
|
|
118
|
+
v-if="progressText !== undefined"
|
|
119
|
+
#loader
|
|
120
|
+
>
|
|
121
|
+
<v-progress-circular
|
|
122
|
+
:indeterminate="progressPercentage === undefined"
|
|
123
|
+
:model-value="progressPercentage"
|
|
124
|
+
size="20"
|
|
125
|
+
width="2"
|
|
126
|
+
/>
|
|
127
|
+
<span class="ml-2">{{ progressText }}</span>
|
|
128
|
+
</template>
|
|
63
129
|
<slot
|
|
64
130
|
:loading="command.waiting"
|
|
65
131
|
:disabled="isDisabled"
|
|
66
132
|
:label="command.label"
|
|
67
133
|
:title="title ?? command.action"
|
|
68
134
|
>
|
|
69
|
-
|
|
135
|
+
{{ command.label }}
|
|
70
136
|
</slot>
|
|
71
137
|
</v-btn>
|
|
72
138
|
<v-btn
|
|
73
139
|
v-else-if="command.allowed"
|
|
74
140
|
v-bind="$attrs"
|
|
75
141
|
:loading="command.waiting"
|
|
142
|
+
:disabled="isDisabled"
|
|
76
143
|
:aria-disabled="isDisabled"
|
|
77
144
|
:title="title ?? command.action"
|
|
78
145
|
:class="{ 'v-btn--disabled': isDisabled }"
|
|
79
146
|
@click="handleClick"
|
|
80
|
-
|
|
147
|
+
>
|
|
148
|
+
<template
|
|
149
|
+
v-if="progressText !== undefined"
|
|
150
|
+
#loader
|
|
151
|
+
>
|
|
152
|
+
<v-progress-circular
|
|
153
|
+
:indeterminate="progressPercentage === undefined"
|
|
154
|
+
:model-value="progressPercentage"
|
|
155
|
+
size="20"
|
|
156
|
+
width="2"
|
|
157
|
+
/>
|
|
158
|
+
<span class="ml-2">{{ progressText }}</span>
|
|
159
|
+
</template>
|
|
160
|
+
</v-btn>
|
|
81
161
|
</template>
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { CauseException } from "effect-app/client"
|
|
3
|
+
import * as Cause from "effect/Cause"
|
|
4
|
+
import { onErrorCaptured, shallowRef } from "vue"
|
|
5
|
+
import { useRouter } from "vue-router"
|
|
6
|
+
|
|
7
|
+
// withDefaults, NOT `props.enabled ?? true`: Vue's boolean casting resolves an
|
|
8
|
+
// ABSENT boolean prop to `false`, so the `??` fallback would never fire and an
|
|
9
|
+
// omitted `enabled` (the frontend case) would silently disable the boundary.
|
|
10
|
+
const props = withDefaults(
|
|
11
|
+
defineProps<{
|
|
12
|
+
captureException: (err: Error, hint?: { extra?: Record<string, unknown> }) => void
|
|
13
|
+
toastError: (message: string) => void
|
|
14
|
+
debug: boolean
|
|
15
|
+
/** register the boundary only when true (backend passes import.meta.client; frontend omits → true) */
|
|
16
|
+
enabled?: boolean
|
|
17
|
+
/** defer handling until app ready (backend: onNuxtReady while hydrating; frontend: omit → run now) */
|
|
18
|
+
scheduleAfterReady?: (fn: () => void) => void
|
|
19
|
+
/** extra side-effect after emit, before error.value is set (backend: nuxtApp vue:error hook) */
|
|
20
|
+
onHandled?: (err: Error, instance: unknown, info: string) => void
|
|
21
|
+
}>(),
|
|
22
|
+
{ enabled: true }
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
defineOptions({ name: "NuxtErrorBoundary", inheritAttrs: false })
|
|
26
|
+
|
|
27
|
+
const emit = defineEmits<{ error: [error: Error] }>()
|
|
28
|
+
|
|
29
|
+
defineSlots<{
|
|
30
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
31
|
+
error(props: { error: Error; clearError: () => void }): any
|
|
32
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
33
|
+
default(): any
|
|
34
|
+
}>()
|
|
35
|
+
|
|
36
|
+
const error = shallowRef<Error | null>(null)
|
|
37
|
+
function clearError() {
|
|
38
|
+
error.value = null
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// lol in production these are different...
|
|
42
|
+
const supportedErrors = ["https://vuejs.org/error-reference/#runtime-0", 0, "0", "setup function"]
|
|
43
|
+
|
|
44
|
+
function handleError(...args: Parameters<Parameters<typeof onErrorCaptured<Error>>[0]>) {
|
|
45
|
+
const [err, instance, info] = args
|
|
46
|
+
const fiberFailure = err instanceof CauseException ? err : null
|
|
47
|
+
// PRO: log that we hit the error boundary
|
|
48
|
+
console.warn(
|
|
49
|
+
"NuxtErrorBoundary caught error in " + info + ": " + (fiberFailure ? "CauseException" : "classic error"),
|
|
50
|
+
fiberFailure ? Cause.pretty(fiberFailure.originalCause) : err,
|
|
51
|
+
instance
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
// PRO: we don't handle native event handlers the same way we handle setup errors,
|
|
55
|
+
// because these errors should only get reported, not take over the page.
|
|
56
|
+
if (!supportedErrors.includes(info)) {
|
|
57
|
+
props.captureException(err, { extra: { info, instance } })
|
|
58
|
+
if (props.debug) {
|
|
59
|
+
props.toastError("An unexpected error has occurred: " + err.toString())
|
|
60
|
+
}
|
|
61
|
+
return
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// PRO: don't render interruptions..
|
|
65
|
+
// e.g when we run useSuspenseQuery, and we navigate away before a query is finished, we get a CancelledError
|
|
66
|
+
// if we however render it here instead of the default slot, we will show the cancellation error of the previous page, instead of rendering the new page
|
|
67
|
+
//
|
|
68
|
+
// NOTE the engine-side contract that makes this blanket silence safe: `useSuspenseQuery(New)`
|
|
69
|
+
// converts an interrupt that settles while the component is STILL MOUNTED (a query-side interrupt,
|
|
70
|
+
// e.g. a server RPC interrupt Exit — the thing that used to blank the page) into a typed
|
|
71
|
+
// `SuspenseInterruptedError` (@effect-app/vue makeClient), whose cause is NOT interrupt-only and
|
|
72
|
+
// therefore falls through to the error slot below. What remains interrupt-only here is genuinely
|
|
73
|
+
// "navigated away / unmounted" — silencing it is correct and avoids rendering the previous page's
|
|
74
|
+
// cancellation over the new page.
|
|
75
|
+
if (fiberFailure && Cause.hasInterruptsOnly(fiberFailure.originalCause)) {
|
|
76
|
+
return
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
emit("error", err)
|
|
80
|
+
props.onHandled?.(err, instance, info)
|
|
81
|
+
error.value = err
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (props.enabled) {
|
|
85
|
+
onErrorCaptured((err, instance, info) => {
|
|
86
|
+
if (props.scheduleAfterReady) props.scheduleAfterReady(() => handleError(err, instance, info))
|
|
87
|
+
else handleError(err, instance, info)
|
|
88
|
+
return false
|
|
89
|
+
})
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// PRO: fix error not clearing after route change: https://github.com/nuxt/nuxt/issues/15781#issuecomment-2320928163
|
|
93
|
+
const router = useRouter()
|
|
94
|
+
router.afterEach(() => {
|
|
95
|
+
clearError()
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
defineExpose({ error, clearError })
|
|
99
|
+
</script>
|
|
100
|
+
|
|
101
|
+
<template>
|
|
102
|
+
<slot
|
|
103
|
+
v-if="error"
|
|
104
|
+
v-bind="{ error, clearError }"
|
|
105
|
+
name="error"
|
|
106
|
+
/>
|
|
107
|
+
|
|
108
|
+
<slot
|
|
109
|
+
v-else
|
|
110
|
+
name="default"
|
|
111
|
+
/>
|
|
112
|
+
</template>
|
|
@@ -43,7 +43,7 @@ export type InputProps<From extends Record<PropertyKey, any>, TName extends Deep
|
|
|
43
43
|
inputClass: string | undefined | null
|
|
44
44
|
}
|
|
45
45
|
field: OmegaFieldInternalApi<From, TName>
|
|
46
|
-
/**
|
|
46
|
+
/** reactive field state, sourced from `field.state` (the Field slot's own `state` prop is a stale snapshot since @tanstack/vue-form 1.32) */
|
|
47
47
|
state: OmegaFieldInternalApi<From, TName>["state"]
|
|
48
48
|
}
|
|
49
49
|
|
|
@@ -3,24 +3,23 @@
|
|
|
3
3
|
:is="form.Field"
|
|
4
4
|
:name="name"
|
|
5
5
|
>
|
|
6
|
-
|
|
6
|
+
<!-- `field.state` is reactive; the Field slot's `state` prop is a stale snapshot since @tanstack/vue-form 1.32 -->
|
|
7
|
+
<template #default="{ field }">
|
|
7
8
|
<slot
|
|
8
9
|
name="pre-array"
|
|
9
|
-
v-bind="{ field, state }"
|
|
10
|
+
v-bind="{ field, state: field.state }"
|
|
10
11
|
/>
|
|
11
12
|
<component
|
|
12
13
|
:is="form.Field"
|
|
13
14
|
v-for="(_, i) of items"
|
|
14
15
|
:key="`${name}[${Number(i)}]`"
|
|
15
|
-
:name="
|
|
16
|
-
`${name}[${Number(i)}]` as DeepKeys<From>
|
|
17
|
-
"
|
|
16
|
+
:name="`${name}[${Number(i)}]` as DeepKeys<From>"
|
|
18
17
|
>
|
|
19
|
-
<template #default="{ field: subField
|
|
18
|
+
<template #default="{ field: subField }">
|
|
20
19
|
<slot
|
|
21
20
|
v-bind="{
|
|
22
21
|
subField,
|
|
23
|
-
subState,
|
|
22
|
+
subState: subField.state,
|
|
24
23
|
index: Number(i),
|
|
25
24
|
field
|
|
26
25
|
}"
|
|
@@ -29,7 +28,7 @@
|
|
|
29
28
|
</component>
|
|
30
29
|
<slot
|
|
31
30
|
name="post-array"
|
|
32
|
-
v-bind="{ field, state }"
|
|
31
|
+
v-bind="{ field, state: field.state }"
|
|
33
32
|
/>
|
|
34
33
|
<!-- TODO: legacy slot, remove this slot -->
|
|
35
34
|
<slot
|
|
@@ -46,7 +45,7 @@
|
|
|
46
45
|
>
|
|
47
46
|
import { type DeepKeys } from "@tanstack/vue-form"
|
|
48
47
|
import { computed, onMounted, provide } from "vue"
|
|
49
|
-
import { type OmegaArrayProps } from "./
|
|
48
|
+
import { type OmegaArrayProps } from "./types"
|
|
50
49
|
|
|
51
50
|
const props = defineProps<OmegaArrayProps<From, To, Name>>()
|
|
52
51
|
|
|
@@ -20,10 +20,10 @@
|
|
|
20
20
|
Name extends DeepKeys<From>"
|
|
21
21
|
>
|
|
22
22
|
import { type DeepKeys } from "@tanstack/vue-form"
|
|
23
|
-
import { pipe } from "effect/Function"
|
|
24
23
|
import * as Order from "effect/Order"
|
|
25
24
|
import { computed } from "vue"
|
|
26
|
-
import { type FieldMeta
|
|
25
|
+
import { type FieldMeta } from "./meta/types"
|
|
26
|
+
import { type FieldPath, type OmegaAutoGenMeta, type OmegaInputProps } from "./types"
|
|
27
27
|
|
|
28
28
|
type NewMeta = OmegaAutoGenMeta<From, To, Name>
|
|
29
29
|
|
|
@@ -71,37 +71,33 @@ const orderBy: Order.Order<NewMeta> = Order.mapInput(
|
|
|
71
71
|
(x: NewMeta) => namePosition(x.name, props.order || [])
|
|
72
72
|
)
|
|
73
73
|
|
|
74
|
-
const children = computed<NewMeta[]>(() =>
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
filterRecord((_, metaKey) =>
|
|
74
|
+
const children = computed<NewMeta[]>(() => {
|
|
75
|
+
const included = filterRecord((value, metaKey) =>
|
|
76
|
+
Boolean(value)
|
|
77
|
+
&& (
|
|
79
78
|
props.pick
|
|
80
79
|
? props.pick.includes(metaKey) && !props.omit?.includes(metaKey)
|
|
81
80
|
: !props.omit?.includes(metaKey)
|
|
82
|
-
)
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
props.sort ? (items: NewMeta[]) => [...items].sort((a, b) => props.sort!(a, b)) : (x: NewMeta[]) => x
|
|
103
|
-
)
|
|
104
|
-
)
|
|
81
|
+
)
|
|
82
|
+
)(props.form.meta as Record<DeepKeys<From>, FieldMeta | undefined>) as Record<DeepKeys<From>, FieldMeta>
|
|
83
|
+
|
|
84
|
+
const withLabels = mapObject((metaValue: FieldMeta, metaKey) => ({
|
|
85
|
+
name: metaKey,
|
|
86
|
+
label: props.labelMap?.(metaKey) || metaKey,
|
|
87
|
+
...metaValue
|
|
88
|
+
}))(included) as unknown as Record<DeepKeys<From>, NewMeta>
|
|
89
|
+
|
|
90
|
+
const filtered = props.filterMap
|
|
91
|
+
? filterMapRecord((m: NewMeta) => {
|
|
92
|
+
const result = props.filterMap?.(m.name, m)
|
|
93
|
+
return result === undefined || result === true ? m : result
|
|
94
|
+
})(withLabels) as Record<DeepKeys<From>, NewMeta>
|
|
95
|
+
: withLabels
|
|
96
|
+
|
|
97
|
+
const sorted = [...Object.values(filtered) as NewMeta[]].sort(orderBy)
|
|
98
|
+
|
|
99
|
+
return props.sort ? sorted.sort(props.sort) : sorted
|
|
100
|
+
})
|
|
105
101
|
|
|
106
102
|
defineSlots<{
|
|
107
103
|
default(props: { child: NewMeta }): void
|
|
@@ -102,7 +102,7 @@
|
|
|
102
102
|
import type { StandardSchemaV1Issue } from "@tanstack/vue-form"
|
|
103
103
|
import { computed, getCurrentInstance } from "vue"
|
|
104
104
|
import { useIntl } from "../../utils"
|
|
105
|
-
import { type OmegaError } from "./
|
|
105
|
+
import { type OmegaError } from "./types"
|
|
106
106
|
|
|
107
107
|
const instance = getCurrentInstance()
|
|
108
108
|
const vuetified = instance?.appContext.components["VAlert"]
|
|
@@ -130,10 +130,9 @@ const showedGeneralErrors = computed(() => {
|
|
|
130
130
|
.flatMap((issues) =>
|
|
131
131
|
issues
|
|
132
132
|
.filter(
|
|
133
|
-
(issue): issue is StandardSchemaV1Issue & { message: string } =>
|
|
134
|
-
typeof (issue as { message?: unknown })?.message === "string"
|
|
133
|
+
(issue): issue is StandardSchemaV1Issue & { message: string } => Boolean(issue?.message)
|
|
135
134
|
)
|
|
136
|
-
.map((issue) =>
|
|
135
|
+
.map((issue) => issue.message)
|
|
137
136
|
)
|
|
138
137
|
)
|
|
139
138
|
})
|
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
import { type DeepKeys } from "@tanstack/vue-form"
|
|
18
18
|
import { inject } from "vue"
|
|
19
19
|
import type { MergedInputProps } from "./InputProps"
|
|
20
|
-
import type { BaseProps, DefaultTypeProps, OmegaInputProps } from "./OmegaFormStuff"
|
|
21
20
|
import OmegaInput from "./OmegaInput.vue"
|
|
21
|
+
import type { BaseProps, DefaultTypeProps, OmegaInputProps } from "./types"
|
|
22
22
|
import { OmegaFormKey } from "./useOmegaForm"
|
|
23
23
|
|
|
24
24
|
const form = inject(OmegaFormKey) as unknown as OmegaInputProps<
|
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<component
|
|
3
3
|
:is="form.Field"
|
|
4
|
-
:key="fieldKey"
|
|
5
4
|
:name="name"
|
|
6
|
-
:validators="
|
|
7
|
-
onChange: schema,
|
|
8
|
-
...validators
|
|
9
|
-
}"
|
|
5
|
+
:validators="validators"
|
|
10
6
|
>
|
|
11
|
-
|
|
7
|
+
<!--
|
|
8
|
+
Read `state` from `field.state`, not the Field slot's `state` prop.
|
|
9
|
+
Since @tanstack/vue-form 1.32 the slot `state` is a one-time snapshot
|
|
10
|
+
taken when the field mounts and never updates, whereas `field.state`
|
|
11
|
+
is a reactive getter backed by the field store.
|
|
12
|
+
-->
|
|
13
|
+
<template #default="{ field }">
|
|
12
14
|
<OmegaInternalInput
|
|
13
15
|
v-if="meta"
|
|
14
|
-
v-bind="{ ...$attrs,
|
|
15
|
-
:field="field"
|
|
16
|
-
:state="state"
|
|
16
|
+
v-bind="{ ...$attrs, ...internalInputProps, inputClass: computedClass }"
|
|
17
|
+
:field="field as any"
|
|
18
|
+
:state="field.state"
|
|
17
19
|
:register="form.registerField"
|
|
18
20
|
:label="label ?? errori18n(propsName)"
|
|
19
21
|
:meta="meta"
|
|
@@ -40,14 +42,24 @@
|
|
|
40
42
|
lang="ts"
|
|
41
43
|
generic="From extends Record<PropertyKey, any>, To extends Record<PropertyKey, any>, Name extends DeepKeys<From>"
|
|
42
44
|
>
|
|
45
|
+
/* eslint-disable @typescript-eslint/no-explicit-any -- TanStack Form Field generic interop and slot prop typing */
|
|
43
46
|
import { type DeepKeys } from "@tanstack/vue-form"
|
|
44
47
|
import { computed, inject, type Ref, useAttrs } from "vue"
|
|
45
|
-
import {
|
|
46
|
-
import { type FieldMeta
|
|
48
|
+
import { useErrorLabel } from "./errors"
|
|
49
|
+
import { type FieldMeta } from "./meta/types"
|
|
47
50
|
import OmegaInternalInput from "./OmegaInternalInput.vue"
|
|
48
|
-
import {
|
|
51
|
+
import { type OmegaInputPropsBase } from "./types"
|
|
49
52
|
|
|
50
|
-
const props = defineProps<OmegaInputPropsBase<From, To, Name>>()
|
|
53
|
+
const props = withDefaults(defineProps<OmegaInputPropsBase<From, To, Name>>(), {
|
|
54
|
+
required: undefined
|
|
55
|
+
})
|
|
56
|
+
const internalInputProps = computed(() => ({
|
|
57
|
+
label: props.label,
|
|
58
|
+
required: props.required,
|
|
59
|
+
type: props.type,
|
|
60
|
+
options: props.options,
|
|
61
|
+
inputs: props.form.inputs
|
|
62
|
+
}))
|
|
51
63
|
|
|
52
64
|
// downgrade to *as* DeepKeys<From> to avoid useless and possible infinite recursion in TS
|
|
53
65
|
const propsName = computed(() => props.name as DeepKeys<From>)
|
|
@@ -57,13 +69,10 @@ defineSlots<{
|
|
|
57
69
|
default?: (props: any) => any
|
|
58
70
|
}>()
|
|
59
71
|
|
|
60
|
-
defineOptions({
|
|
61
|
-
inheritAttrs: false
|
|
62
|
-
})
|
|
72
|
+
defineOptions({ inheritAttrs: false })
|
|
63
73
|
|
|
64
74
|
const attrs = useAttrs()
|
|
65
75
|
|
|
66
|
-
// Compute the class to use based on inputClass prop
|
|
67
76
|
const computedClass = computed(() => {
|
|
68
77
|
if (props.inputClass === null) return undefined
|
|
69
78
|
if (props.inputClass !== undefined) return props.inputClass
|
|
@@ -82,28 +91,5 @@ const meta = computed(() => {
|
|
|
82
91
|
return props.form.meta[propsName.value]
|
|
83
92
|
})
|
|
84
93
|
|
|
85
|
-
// Key to force Field re-mount when meta type changes (for TaggedUnion support)
|
|
86
|
-
const fieldKey = computed(() => {
|
|
87
|
-
const m = meta.value
|
|
88
|
-
if (!m) return propsName.value
|
|
89
|
-
// Include type and key constraints in the key so Field re-mounts when validation rules change
|
|
90
|
-
// Cast to any since not all FieldMeta variants have these properties
|
|
91
|
-
const fm = m as any
|
|
92
|
-
return `${propsName.value}-${fm.type}-${fm.minLength ?? ""}-${fm.maxLength ?? ""}-${fm.minimum ?? ""}-${
|
|
93
|
-
fm.maximum ?? ""
|
|
94
|
-
}`
|
|
95
|
-
})
|
|
96
|
-
|
|
97
|
-
// Call useIntl during setup to avoid issues when computed re-evaluates
|
|
98
|
-
const { trans } = useIntl()
|
|
99
|
-
|
|
100
|
-
const schema = computed(() => {
|
|
101
|
-
if (!meta.value) {
|
|
102
|
-
console.log(props.name, Object.keys(props.form.meta), props.form.meta)
|
|
103
|
-
throw new Error("Meta is undefined")
|
|
104
|
-
}
|
|
105
|
-
return generateInputStandardSchemaFromFieldMeta(meta.value, trans)
|
|
106
|
-
})
|
|
107
|
-
|
|
108
94
|
const errori18n = useErrorLabel(props.form)
|
|
109
95
|
</script>
|