@effect-app/vue-components 4.0.0-beta.314 → 4.0.0-beta.316

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 (57) hide show
  1. package/dist/types/components/Dialog.vue.d.ts +1 -1
  2. package/dist/types/components/OmegaForm/InputProps.d.ts +1 -0
  3. package/dist/types/components/OmegaForm/OmegaInputVuetify.vue.d.ts +6 -0
  4. package/dist/types/components/OmegaForm/OmegaInternalInput.vue.d.ts +1 -0
  5. package/dist/types/components/OmegaForm/decimalSeparatorInput.d.ts +13 -0
  6. package/dist/vue-components.es.js +9 -9
  7. package/dist/vue-components21.es.js +14 -4
  8. package/dist/vue-components32.es.js +14 -23
  9. package/dist/vue-components33.es.js +23 -299
  10. package/dist/vue-components34.es.js +344 -0
  11. package/dist/vue-components36.es.js +7 -72
  12. package/dist/vue-components37.es.js +72 -3
  13. package/dist/vue-components38.es.js +4 -63
  14. package/dist/vue-components39.es.js +63 -4
  15. package/dist/vue-components40.es.js +4 -23
  16. package/dist/vue-components41.es.js +23 -4
  17. package/dist/vue-components42.es.js +3 -57
  18. package/dist/vue-components43.es.js +57 -3
  19. package/dist/vue-components44.es.js +4 -11
  20. package/dist/vue-components45.es.js +11 -21
  21. package/dist/vue-components46.es.js +22 -0
  22. package/dist/vue-components48.es.js +8 -3
  23. package/dist/vue-components49.es.js +3 -37
  24. package/dist/vue-components50.es.js +34 -23
  25. package/dist/vue-components51.es.js +23 -24
  26. package/dist/vue-components52.es.js +27 -6
  27. package/dist/vue-components53.es.js +6 -17
  28. package/dist/vue-components54.es.js +16 -34
  29. package/dist/vue-components55.es.js +34 -16
  30. package/dist/vue-components56.es.js +16 -19
  31. package/dist/vue-components57.es.js +10 -19
  32. package/dist/vue-components58.es.js +29 -6
  33. package/dist/vue-components59.es.js +6 -8
  34. package/dist/vue-components60.es.js +7 -36
  35. package/dist/vue-components61.es.js +37 -24
  36. package/dist/vue-components62.es.js +19 -122
  37. package/dist/vue-components63.es.js +126 -22
  38. package/dist/vue-components64.es.js +20 -17
  39. package/dist/vue-components65.es.js +18 -6
  40. package/dist/vue-components66.es.js +5 -15
  41. package/dist/vue-components67.es.js +18 -4
  42. package/dist/vue-components68.es.js +4 -28
  43. package/dist/vue-components69.es.js +28 -4
  44. package/dist/vue-components70.es.js +4 -42
  45. package/dist/vue-components71.es.js +38 -96
  46. package/dist/vue-components72.es.js +95 -30
  47. package/dist/vue-components73.es.js +35 -18
  48. package/dist/vue-components74.es.js +18 -49
  49. package/dist/vue-components75.es.js +50 -0
  50. package/package.json +3 -3
  51. package/src/components/OmegaForm/InputProps.ts +1 -0
  52. package/src/components/OmegaForm/OmegaInputVuetify.vue +65 -9
  53. package/src/components/OmegaForm/OmegaInternalInput.vue +1 -0
  54. package/src/components/OmegaForm/decimalSeparatorInput.ts +38 -0
  55. package/src/components/OmegaForm/validation/localized.ts +15 -4
  56. package/dist/vue-components35.es.js +0 -8
  57. package/dist/vue-components47.es.js +0 -9
@@ -87,20 +87,54 @@
87
87
  />
88
88
  </template>
89
89
  </v-textarea>
90
- <component
91
- :is="inputProps.type === 'range' ? 'v-slider' : 'v-text-field'"
92
- v-if="inputProps.type === 'number' || inputProps.type === 'range'"
90
+ <!-- min/max are deliberately NOT passed as VNumberInput props: the component
91
+ silently withholds out-of-range values from the model (so schema errors
92
+ would never show) and clamps on blur. Validation stays schema-driven; the
93
+ bounds are exposed to AT via the spinbutton ARIA attrs, which fall through
94
+ to the native input. Same reasoning for precision: Vuetify's default (0)
95
+ would silently block decimals, so it's forced to null and typing 1.5 into
96
+ an int field shows the schema error instead. -->
97
+ <v-number-input
98
+ v-if="inputProps.type === 'number'"
93
99
  :id="inputProps.id"
94
100
  :required="inputProps.required"
95
- :min="inputProps.min"
96
- :max="inputProps.max"
97
- :type="inputProps.type"
101
+ role="spinbutton"
102
+ :aria-valuemin="typeof inputProps.min === 'number' ? inputProps.min : undefined"
103
+ :aria-valuemax="typeof inputProps.max === 'number' ? inputProps.max : undefined"
104
+ :aria-valuenow="typeof state.value === 'number' ? state.value : undefined"
98
105
  :name="field.name"
99
106
  :label="inputProps.label"
100
107
  :error-messages="inputProps.errorMessages"
101
108
  :error="inputProps.error"
109
+ :precision="null"
110
+ control-variant="stacked"
102
111
  v-bind="$attrs"
103
- :model-value="state.value"
112
+ :model-value="state.value as any"
113
+ @beforeinput.capture="onNumberBeforeinput"
114
+ @update:model-value="(v: number | null) => field.handleChange((v ?? undefined) as any)"
115
+ >
116
+ <template
117
+ v-if="$slots.label"
118
+ #label
119
+ >
120
+ <slot
121
+ name="label"
122
+ v-bind="{ required: inputProps.required, id: inputProps.id, label: inputProps.label }"
123
+ />
124
+ </template>
125
+ </v-number-input>
126
+ <v-slider
127
+ v-if="inputProps.type === 'range'"
128
+ :id="inputProps.id"
129
+ :required="inputProps.required"
130
+ :min="typeof inputProps.min === 'number' ? inputProps.min : undefined"
131
+ :max="typeof inputProps.max === 'number' ? inputProps.max : undefined"
132
+ :name="field.name"
133
+ :label="inputProps.label"
134
+ :error-messages="inputProps.errorMessages"
135
+ :error="inputProps.error"
136
+ v-bind="$attrs"
137
+ :model-value="state.value as any"
104
138
  @update:model-value="(e: any) => {
105
139
  if (e || e === 0) {
106
140
  field.handleChange(Number(e) as any)
@@ -118,7 +152,7 @@
118
152
  v-bind="{ required: inputProps.required, id: inputProps.id, label: inputProps.label }"
119
153
  />
120
154
  </template>
121
- </component>
155
+ </v-slider>
122
156
  <template v-if="inputProps.type === 'radio'">
123
157
  <v-radio-group
124
158
  :id="inputProps.id"
@@ -239,7 +273,9 @@
239
273
  generic="From extends Record<PropertyKey, any>, Name extends DeepKeys<From>"
240
274
  >
241
275
  import { type DeepKeys } from "@tanstack/vue-form"
242
- import { computed, watchEffect } from "vue"
276
+ import { computed, useAttrs, watchEffect } from "vue"
277
+ import { useLocale } from "vuetify"
278
+ import { handleDecimalSeparatorBeforeinput } from "./decimalSeparatorInput"
243
279
  import type { VuetifyInputProps } from "./InputProps"
244
280
  import { getInputType } from "./inputs"
245
281
  import { typeOverrides } from "./types"
@@ -255,6 +291,26 @@ defineOptions({
255
291
  inheritAttrs: false
256
292
  })
257
293
 
294
+ const attrs = useAttrs()
295
+ // useLocale throws outside a Vuetify app (e.g. tests mounting with stubbed
296
+ // vuetify components); numbers then fall back to the "." separator.
297
+ const localeDecimalSeparator = (() => {
298
+ try {
299
+ return useLocale().decimalSeparator
300
+ } catch {
301
+ return undefined
302
+ }
303
+ })()
304
+
305
+ // Mirrors VNumberInput's own resolution: an explicit decimal-separator attr
306
+ // wins, otherwise the locale decides.
307
+ const decimalSeparator = computed(() => {
308
+ const explicit = (attrs["decimal-separator"] ?? attrs["decimalSeparator"]) as string | undefined
309
+ return explicit?.[0] || localeDecimalSeparator?.value || "."
310
+ })
311
+
312
+ const onNumberBeforeinput = (e: Event) => handleDecimalSeparatorBeforeinput(e as InputEvent, decimalSeparator.value)
313
+
258
314
  // True when no dedicated branch handles `inputProps.type` (it's outside the
259
315
  // built-in `typeOverrides`): the fallback text input renders and we warn.
260
316
  const isUnhandledType = computed(() => !(typeOverrides as readonly string[]).includes(props.inputProps.type))
@@ -191,6 +191,7 @@ const inputProps: ComputedRef<InputProps<From, Name>> = computed(() => ({
191
191
  ? (props.meta?.minimum
192
192
  ?? (typeof props.meta?.exclusiveMinimum === "number" ? props.meta.exclusiveMinimum + 1 : undefined))
193
193
  : undefined,
194
+ refinement: props.meta?.type === "number" ? props.meta?.refinement : undefined,
194
195
  errorMessages: errors.value,
195
196
  error: !!errors.value.length,
196
197
  type: fieldType.value,
@@ -0,0 +1,38 @@
1
+ const escapeForRegex = (s: string) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
2
+
3
+ /**
4
+ * VNumberInput only accepts the active decimal separator and silently drops
5
+ * the other one while typing. Attached in capture phase on the component
6
+ * root, this handler lets users type either "." or ",": the wrong character
7
+ * is translated to the active separator before Vuetify's own beforeinput
8
+ * filter can reject it.
9
+ *
10
+ * No validation beyond Vuetify's own single-separator typing rule: OmegaForm
11
+ * deliberately lets out-of-schema values through so schema errors show,
12
+ * instead of silently blocking input (same reasoning as min/max not being
13
+ * passed as props).
14
+ */
15
+ export const handleDecimalSeparatorBeforeinput = (e: InputEvent, separator: string) => {
16
+ if (!e.data) return
17
+ // The active separator may be any single char (locale or explicit prop,
18
+ // e.g. "٫"); everything from [".", ","] that isn't it gets translated.
19
+ const wrongs = [".", ","].filter((c) => c !== separator)
20
+ if (!wrongs.some((c) => e.data!.includes(c))) return
21
+
22
+ const input = e.target as HTMLInputElement
23
+ e.preventDefault()
24
+ e.stopPropagation()
25
+
26
+ const data = wrongs.reduce((acc, c) => acc.replaceAll(c, separator), e.data)
27
+ const start = input.selectionStart ?? input.value.length
28
+ const end = input.selectionEnd ?? input.value.length
29
+ const next = input.value.slice(0, start) + data + input.value.slice(end)
30
+ // Mirrors Vuetify's own typing filter: at most one separator, "-" only at
31
+ // the start. A second separator would only produce an unparseable string.
32
+ if (!new RegExp(`^-?\\d*${escapeForRegex(separator)}?\\d*$`).test(next)) return
33
+
34
+ input.value = next
35
+ const cursor = start + data.length
36
+ input.setSelectionRange(cursor, cursor)
37
+ input.dispatchEvent(new Event("input", { bubbles: true }))
38
+ }
@@ -13,6 +13,7 @@ type FilterMeta =
13
13
  | { readonly _tag: "isGreaterThan"; readonly exclusiveMinimum: number }
14
14
  | { readonly _tag: "isLessThanOrEqualTo"; readonly maximum: number }
15
15
  | { readonly _tag: "isLessThan"; readonly exclusiveMaximum: number }
16
+ | { readonly _tag: "isBetween"; readonly minimum: number; readonly maximum: number }
16
17
  | { readonly _tag?: undefined }
17
18
 
18
19
  /** Map filter annotations (representation since beta.107, legacy meta before) to FilterMeta. */
@@ -85,20 +86,30 @@ export const makeStandardSchemaV1Hooks = (
85
86
  const actual = input !== undefined ? String(input) : "NaN"
86
87
  return trans("validation.integer.expected", { actualValue: actual })
87
88
  }
89
+ // isExclusive semantics in the message catalog: true = strict bound
90
+ // ("greater/less than"), false = inclusive bound ("at least/at most").
88
91
  case "isGreaterThanOrEqualTo":
89
92
  return trans(
90
93
  meta.minimum === 0 ? "validation.number.positive" : "validation.number.min",
91
- { minimum: meta.minimum, isExclusive: true }
94
+ { minimum: meta.minimum, isExclusive: false }
92
95
  )
93
96
  case "isGreaterThan":
94
97
  return trans(
95
98
  meta.exclusiveMinimum === 0 ? "validation.number.positive" : "validation.number.min",
96
- { minimum: meta.exclusiveMinimum, isExclusive: false }
99
+ { minimum: meta.exclusiveMinimum, isExclusive: true }
97
100
  )
98
101
  case "isLessThanOrEqualTo":
99
- return trans("validation.number.max", { maximum: meta.maximum, isExclusive: true })
102
+ return trans("validation.number.max", { maximum: meta.maximum, isExclusive: false })
100
103
  case "isLessThan":
101
- return trans("validation.number.max", { maximum: meta.exclusiveMaximum, isExclusive: false })
104
+ return trans("validation.number.max", { maximum: meta.exclusiveMaximum, isExclusive: true })
105
+ case "isBetween": {
106
+ // Inclusive on both ends; report the side the value actually violated.
107
+ const input = reportedInput(issue)
108
+ const below = typeof input === "number" && input < meta.minimum
109
+ return below
110
+ ? trans("validation.number.min", { minimum: meta.minimum, isExclusive: false })
111
+ : trans("validation.number.max", { maximum: meta.maximum, isExclusive: false })
112
+ }
102
113
  default:
103
114
  // Fall back to the default check hook so custom S.makeFilter messages
104
115
  // (which surface as InvalidValue.annotations.message on issue.issue)
@@ -1,8 +0,0 @@
1
- (function(){try{if(typeof document<`u`){var e=document.createElement(`style`);if(e.appendChild(document.createTextNode(`.omega-input .v-input__details:has(.v-messages:empty){grid-template-rows:0fr;transition:all .2s}.omega-input .v-messages:empty{min-height:0}.omega-input .v-input__details:has(.v-messages){grid-template-rows:1fr;min-height:0;transition:all .2s;display:grid;overflow:hidden}.omega-input .v-messages{transition:all .2s}.omega-input .v-messages>*{transition-duration:0s!important}.omega-input [role=alert]:has(.v-messages:empty){padding:0}.omega-input .v-btn{cursor:pointer;appearance:none;width:auto;box-shadow:none;min-width:auto;height:auto;padding:.5em .5em .5em 1em;display:block}`)),document.head.appendChild(e),window.customElements){let e=window.customElements.define;window.customElements.define=function(t,n){let r=n.prototype.connectedCallback;return n.prototype.connectedCallback=function(){if(r&&r.call(this),this.shadowRoot){let e=document.createElement(`style`);e.appendChild(document.createTextNode(`.omega-input .v-input__details:has(.v-messages:empty){grid-template-rows:0fr;transition:all .2s}.omega-input .v-messages:empty{min-height:0}.omega-input .v-input__details:has(.v-messages){grid-template-rows:1fr;min-height:0;transition:all .2s;display:grid;overflow:hidden}.omega-input .v-messages{transition:all .2s}.omega-input .v-messages>*{transition-duration:0s!important}.omega-input [role=alert]:has(.v-messages:empty){padding:0}.omega-input .v-btn{cursor:pointer;appearance:none;width:auto;box-shadow:none;min-width:auto;height:auto;padding:.5em .5em .5em 1em;display:block}`)),this.shadowRoot.appendChild(e)}},e.call(window.customElements,t,n)}}}}catch(e){console.error(`vite-plugin-css-injected-by-js`,e)}})();
2
- (function(){try{if(typeof document<`u`){var e=document.createElement(`style`);if(e.appendChild(document.createTextNode(`.omega-input .v-input__details:has(.v-messages:empty){grid-template-rows:0fr;transition:all .2s}.omega-input .v-messages:empty{min-height:0}.omega-input .v-input__details:has(.v-messages){grid-template-rows:1fr;min-height:0;transition:all .2s;display:grid;overflow:hidden}.omega-input .v-messages{transition:all .2s}.omega-input .v-messages>*{transition-duration:0s!important}.omega-input [role=alert]:has(.v-messages:empty){padding:0}.omega-input .v-btn{cursor:pointer;appearance:none;width:auto;box-shadow:none;min-width:auto;height:auto;padding:.5em .5em .5em 1em;display:block}`)),document.head.appendChild(e),window.customElements){let e=window.customElements.define;window.customElements.define=function(t,n){let r=n.prototype.connectedCallback;return n.prototype.connectedCallback=function(){if(r&&r.call(this),this.shadowRoot){let e=document.createElement(`style`);e.appendChild(document.createTextNode(`.omega-input .v-input__details:has(.v-messages:empty){grid-template-rows:0fr;transition:all .2s}.omega-input .v-messages:empty{min-height:0}.omega-input .v-input__details:has(.v-messages){grid-template-rows:1fr;min-height:0;transition:all .2s;display:grid;overflow:hidden}.omega-input .v-messages{transition:all .2s}.omega-input .v-messages>*{transition-duration:0s!important}.omega-input [role=alert]:has(.v-messages:empty){padding:0}.omega-input .v-btn{cursor:pointer;appearance:none;width:auto;box-shadow:none;min-width:auto;height:auto;padding:.5em .5em .5em 1em;display:block}`)),this.shadowRoot.appendChild(e)}},e.call(window.customElements,t,n)}}}}catch(e){console.error(`vite-plugin-css-injected-by-js`,e)}})();
3
- import e from "./vue-components33.es.js";
4
-
5
- //#region src/components/OmegaForm/OmegaInputVuetify.vue
6
- var t = e;
7
- //#endregion
8
- export { t as default };
@@ -1,9 +0,0 @@
1
- (function(){try{if(typeof document<`u`){var e=document.createElement(`style`);if(e.appendChild(document.createTextNode(`fieldset[data-v-13a7a1b6]{display:contents}fieldset[disabled][data-v-13a7a1b6]>*{pointer-events:none}`)),document.head.appendChild(e),window.customElements){let e=window.customElements.define;window.customElements.define=function(t,n){let r=n.prototype.connectedCallback;return n.prototype.connectedCallback=function(){if(r&&r.call(this),this.shadowRoot){let e=document.createElement(`style`);e.appendChild(document.createTextNode(`fieldset[data-v-13a7a1b6]{display:contents}fieldset[disabled][data-v-13a7a1b6]>*{pointer-events:none}`)),this.shadowRoot.appendChild(e)}},e.call(window.customElements,t,n)}}}}catch(e){console.error(`vite-plugin-css-injected-by-js`,e)}})();
2
- (function(){try{if(typeof document<`u`){var e=document.createElement(`style`);if(e.appendChild(document.createTextNode(`fieldset[data-v-13a7a1b6]{display:contents}fieldset[disabled][data-v-13a7a1b6]>*{pointer-events:none}`)),document.head.appendChild(e),window.customElements){let e=window.customElements.define;window.customElements.define=function(t,n){let r=n.prototype.connectedCallback;return n.prototype.connectedCallback=function(){if(r&&r.call(this),this.shadowRoot){let e=document.createElement(`style`);e.appendChild(document.createTextNode(`fieldset[data-v-13a7a1b6]{display:contents}fieldset[disabled][data-v-13a7a1b6]>*{pointer-events:none}`)),this.shadowRoot.appendChild(e)}},e.call(window.customElements,t,n)}}}}catch(e){console.error(`vite-plugin-css-injected-by-js`,e)}})();
3
- import e from "./vue-components30.es.js";
4
- import t from "./vue-components45.es.js";
5
-
6
- //#region src/components/OmegaForm/OmegaWrapper.vue
7
- var n = /*#__PURE__*/ e(t, [["__scopeId", "data-v-13a7a1b6"]]);
8
- //#endregion
9
- export { n as default };