@shwfed/config 2.12.10 → 2.12.13

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 (68) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/module.mjs +8 -3
  3. package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.table/config.d.vue.ts +68 -68
  4. package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.table/config.vue.d.ts +68 -68
  5. package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.table/runtime.d.vue.ts +68 -68
  6. package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.table/runtime.vue.d.ts +68 -68
  7. package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.table/schema.d.ts +34 -34
  8. package/dist/runtime/components/form/fields/2026-04-22/com.shwfed.form.field.text/runtime.vue +2 -0
  9. package/dist/runtime/components/form/fields/2026-04-22/com.shwfed.form.field.textarea/runtime.vue +2 -0
  10. package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.date/runtime.vue +2 -0
  11. package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.daterange/runtime.vue +2 -0
  12. package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.datetime/runtime.vue +2 -0
  13. package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.datetimerange/runtime.vue +2 -0
  14. package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.time/runtime.vue +2 -0
  15. package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.timerange/runtime.vue +2 -0
  16. package/dist/runtime/components/form/fields/2026-04-28/com.shwfed.form.field.number/runtime.vue +24 -15
  17. package/dist/runtime/components/form/fields/2026-04-28/com.shwfed.form.field.numberrange/runtime.vue +36 -13
  18. package/dist/runtime/components/form/fields/2026-05-13/com.shwfed.form.field.list/runtime.vue +5 -2
  19. package/dist/runtime/components/form/fields/2026-05-24/com.shwfed.form.field.month/runtime.vue +2 -0
  20. package/dist/runtime/components/form/fields/2026-05-24/com.shwfed.form.field.monthrange/runtime.vue +2 -0
  21. package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.combobox.multi/runtime.vue +2 -0
  22. package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.combobox.single/runtime.vue +2 -0
  23. package/dist/runtime/components/form/fields/2026-05-28/com.shwfed.form.field.tree.multi/runtime.vue +22 -18
  24. package/dist/runtime/components/form/fields/2026-06-09/com.shwfed.form.field.upload/runtime.vue +9 -1
  25. package/dist/runtime/components/form/fields/2026-06-14/com.shwfed.form.field.combobox.multi/runtime.vue +2 -0
  26. package/dist/runtime/components/form/fields/2026-06-14/com.shwfed.form.field.combobox.single/runtime.vue +2 -0
  27. package/dist/runtime/components/form/utils/derived.js +2 -2
  28. package/dist/runtime/components/form/utils/state.d.ts +18 -0
  29. package/dist/runtime/components/form/utils/state.js +15 -2
  30. package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.number-input/runtime.vue +22 -14
  31. package/dist/runtime/components/table/columns/2026-05-24/com.shwfed.table.column.combobox-single.remote.options-remote/config.d.vue.ts +2 -2
  32. package/dist/runtime/components/table/columns/2026-05-24/com.shwfed.table.column.combobox-single.remote.options-remote/config.vue.d.ts +2 -2
  33. package/dist/runtime/components/table/columns/2026-05-24/com.shwfed.table.column.combobox-single.remote.options-static/config.d.vue.ts +2 -2
  34. package/dist/runtime/components/table/columns/2026-05-24/com.shwfed.table.column.combobox-single.remote.options-static/config.vue.d.ts +2 -2
  35. package/dist/runtime/components/table/columns/2026-05-25/com.shwfed.table.column.combobox-multi.remote.options-remote/config.d.vue.ts +2 -2
  36. package/dist/runtime/components/table/columns/2026-05-25/com.shwfed.table.column.combobox-multi.remote.options-remote/config.vue.d.ts +2 -2
  37. package/dist/runtime/components/table/columns/2026-05-25/com.shwfed.table.column.combobox-multi.remote.options-static/config.d.vue.ts +2 -2
  38. package/dist/runtime/components/table/columns/2026-05-25/com.shwfed.table.column.combobox-multi.remote.options-static/config.vue.d.ts +2 -2
  39. package/dist/runtime/components/table/columns/2026-05-26/com.shwfed.table.column.combobox-multi.remote/config.d.vue.ts +2 -2
  40. package/dist/runtime/components/table/columns/2026-05-26/com.shwfed.table.column.combobox-multi.remote/config.vue.d.ts +2 -2
  41. package/dist/runtime/components/table/columns/2026-05-26/com.shwfed.table.column.combobox-single.remote/config.d.vue.ts +2 -2
  42. package/dist/runtime/components/table/columns/2026-05-26/com.shwfed.table.column.combobox-single.remote/config.vue.d.ts +2 -2
  43. package/dist/runtime/components/table/columns/2026-05-27/com.shwfed.table.column.tree-combobox-multi/config.d.vue.ts +2 -2
  44. package/dist/runtime/components/table/columns/2026-05-27/com.shwfed.table.column.tree-combobox-multi/config.vue.d.ts +2 -2
  45. package/dist/runtime/components/table/columns/2026-05-27/com.shwfed.table.column.tree-combobox-single/config.d.vue.ts +2 -2
  46. package/dist/runtime/components/table/columns/2026-05-27/com.shwfed.table.column.tree-combobox-single/config.vue.d.ts +2 -2
  47. package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.combobox-multi/config.d.vue.ts +2 -2
  48. package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.combobox-multi/config.vue.d.ts +2 -2
  49. package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.combobox-single/config.d.vue.ts +2 -2
  50. package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.combobox-single/config.vue.d.ts +2 -2
  51. package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.tree-combobox-multi/config.d.vue.ts +2 -2
  52. package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.tree-combobox-multi/config.vue.d.ts +2 -2
  53. package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.tree-combobox-single/config.d.vue.ts +2 -2
  54. package/dist/runtime/components/table/columns/2026-05-28/com.shwfed.table.column.tree-combobox-single/config.vue.d.ts +2 -2
  55. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.combobox-multi/config.d.vue.ts +2 -2
  56. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.combobox-multi/config.vue.d.ts +2 -2
  57. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.combobox-single/config.d.vue.ts +2 -2
  58. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.combobox-single/config.vue.d.ts +2 -2
  59. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.tree-combobox-multi/config.d.vue.ts +2 -2
  60. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.tree-combobox-multi/config.vue.d.ts +2 -2
  61. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.tree-combobox-single/config.d.vue.ts +2 -2
  62. package/dist/runtime/components/table/columns/2026-06-14/com.shwfed.table.column.tree-combobox-single/config.vue.d.ts +2 -2
  63. package/dist/runtime/components/table/index.vue +2 -2
  64. package/dist/runtime/components/table/schema.d.ts +68 -68
  65. package/dist/runtime/components/ui/field/index.js +6 -1
  66. package/dist/runtime/share/number-format.d.ts +1 -0
  67. package/dist/runtime/share/number-format.js +12 -13
  68. package/package.json +1 -1
@@ -29,40 +29,6 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
29
29
  readonly message: string;
30
30
  }[]] | undefined;
31
31
  }[];
32
- readonly query?: {
33
- readonly style?: string | undefined;
34
- readonly id?: string | undefined;
35
- readonly initial?: {
36
- readonly data: string;
37
- readonly request?: string | undefined;
38
- } | undefined;
39
- readonly displayName?: string | undefined;
40
- readonly kind: "shwfed.component.form";
41
- readonly fields: readonly any[];
42
- readonly layouts: readonly {
43
- readonly name: string;
44
- readonly layout: {
45
- readonly style?: string | undefined;
46
- readonly columns: number;
47
- readonly gap?: number | undefined;
48
- readonly rows?: number | undefined;
49
- readonly placements: {
50
- readonly [x: string]: {
51
- readonly area: readonly [readonly [number, number], readonly [number, number]];
52
- readonly v?: "stretch" | "center" | "end" | "start" | undefined;
53
- readonly h?: "stretch" | "center" | "end" | "start" | undefined;
54
- };
55
- };
56
- };
57
- readonly media?: string | undefined;
58
- }[];
59
- readonly readonly?: string | undefined;
60
- } | undefined;
61
- readonly dataSource?: {
62
- readonly data: string;
63
- readonly total?: string | undefined;
64
- readonly request?: string | undefined;
65
- } | undefined;
66
32
  readonly actions?: {
67
33
  readonly size: "default" | "sm" | "xs";
68
34
  readonly style?: string | undefined;
@@ -130,6 +96,40 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
130
96
  readonly variant?: "default" | "link" | "destructive" | "primary" | "ghost" | undefined;
131
97
  }[];
132
98
  } | undefined;
99
+ readonly query?: {
100
+ readonly style?: string | undefined;
101
+ readonly id?: string | undefined;
102
+ readonly initial?: {
103
+ readonly data: string;
104
+ readonly request?: string | undefined;
105
+ } | undefined;
106
+ readonly displayName?: string | undefined;
107
+ readonly kind: "shwfed.component.form";
108
+ readonly fields: readonly any[];
109
+ readonly layouts: readonly {
110
+ readonly name: string;
111
+ readonly layout: {
112
+ readonly style?: string | undefined;
113
+ readonly columns: number;
114
+ readonly gap?: number | undefined;
115
+ readonly rows?: number | undefined;
116
+ readonly placements: {
117
+ readonly [x: string]: {
118
+ readonly area: readonly [readonly [number, number], readonly [number, number]];
119
+ readonly v?: "stretch" | "center" | "end" | "start" | undefined;
120
+ readonly h?: "stretch" | "center" | "end" | "start" | undefined;
121
+ };
122
+ };
123
+ };
124
+ readonly media?: string | undefined;
125
+ }[];
126
+ readonly readonly?: string | undefined;
127
+ } | undefined;
128
+ readonly dataSource?: {
129
+ readonly data: string;
130
+ readonly total?: string | undefined;
131
+ readonly request?: string | undefined;
132
+ } | undefined;
133
133
  readonly bottomActions?: {
134
134
  readonly size: "default" | "sm" | "xs";
135
135
  readonly style?: string | undefined;
@@ -277,40 +277,6 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
277
277
  readonly message: string;
278
278
  }[]] | undefined;
279
279
  }[];
280
- readonly query?: {
281
- readonly style?: string | undefined;
282
- readonly id?: string | undefined;
283
- readonly initial?: {
284
- readonly data: string;
285
- readonly request?: string | undefined;
286
- } | undefined;
287
- readonly displayName?: string | undefined;
288
- readonly kind: "shwfed.component.form";
289
- readonly fields: readonly any[];
290
- readonly layouts: readonly {
291
- readonly name: string;
292
- readonly layout: {
293
- readonly style?: string | undefined;
294
- readonly columns: number;
295
- readonly gap?: number | undefined;
296
- readonly rows?: number | undefined;
297
- readonly placements: {
298
- readonly [x: string]: {
299
- readonly area: readonly [readonly [number, number], readonly [number, number]];
300
- readonly v?: "stretch" | "center" | "end" | "start" | undefined;
301
- readonly h?: "stretch" | "center" | "end" | "start" | undefined;
302
- };
303
- };
304
- };
305
- readonly media?: string | undefined;
306
- }[];
307
- readonly readonly?: string | undefined;
308
- } | undefined;
309
- readonly dataSource?: {
310
- readonly data: string;
311
- readonly total?: string | undefined;
312
- readonly request?: string | undefined;
313
- } | undefined;
314
280
  readonly actions?: {
315
281
  readonly size: "default" | "sm" | "xs";
316
282
  readonly style?: string | undefined;
@@ -378,6 +344,40 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
378
344
  readonly variant?: "default" | "link" | "destructive" | "primary" | "ghost" | undefined;
379
345
  }[];
380
346
  } | undefined;
347
+ readonly query?: {
348
+ readonly style?: string | undefined;
349
+ readonly id?: string | undefined;
350
+ readonly initial?: {
351
+ readonly data: string;
352
+ readonly request?: string | undefined;
353
+ } | undefined;
354
+ readonly displayName?: string | undefined;
355
+ readonly kind: "shwfed.component.form";
356
+ readonly fields: readonly any[];
357
+ readonly layouts: readonly {
358
+ readonly name: string;
359
+ readonly layout: {
360
+ readonly style?: string | undefined;
361
+ readonly columns: number;
362
+ readonly gap?: number | undefined;
363
+ readonly rows?: number | undefined;
364
+ readonly placements: {
365
+ readonly [x: string]: {
366
+ readonly area: readonly [readonly [number, number], readonly [number, number]];
367
+ readonly v?: "stretch" | "center" | "end" | "start" | undefined;
368
+ readonly h?: "stretch" | "center" | "end" | "start" | undefined;
369
+ };
370
+ };
371
+ };
372
+ readonly media?: string | undefined;
373
+ }[];
374
+ readonly readonly?: string | undefined;
375
+ } | undefined;
376
+ readonly dataSource?: {
377
+ readonly data: string;
378
+ readonly total?: string | undefined;
379
+ readonly request?: string | undefined;
380
+ } | undefined;
381
381
  readonly bottomActions?: {
382
382
  readonly size: "default" | "sm" | "xs";
383
383
  readonly style?: string | undefined;
@@ -47,40 +47,6 @@ export declare function schema(configure: (env: Environment) => void, _blockRef:
47
47
  readonly message: string;
48
48
  }[]] | undefined;
49
49
  }[];
50
- readonly query?: {
51
- readonly style?: string | undefined;
52
- readonly id?: string | undefined;
53
- readonly initial?: {
54
- readonly data: string;
55
- readonly request?: string | undefined;
56
- } | undefined;
57
- readonly displayName?: string | undefined;
58
- readonly kind: "shwfed.component.form";
59
- readonly fields: readonly any[];
60
- readonly layouts: readonly {
61
- readonly name: string;
62
- readonly layout: {
63
- readonly style?: string | undefined;
64
- readonly columns: number;
65
- readonly gap?: number | undefined;
66
- readonly rows?: number | undefined;
67
- readonly placements: {
68
- readonly [x: string]: {
69
- readonly area: readonly [readonly [number, number], readonly [number, number]];
70
- readonly v?: "stretch" | "center" | "end" | "start" | undefined;
71
- readonly h?: "stretch" | "center" | "end" | "start" | undefined;
72
- };
73
- };
74
- };
75
- readonly media?: string | undefined;
76
- }[];
77
- readonly readonly?: string | undefined;
78
- } | undefined;
79
- readonly dataSource?: {
80
- readonly data: string;
81
- readonly total?: string | undefined;
82
- readonly request?: string | undefined;
83
- } | undefined;
84
50
  readonly actions?: {
85
51
  readonly size: "default" | "sm" | "xs";
86
52
  readonly style?: string | undefined;
@@ -148,6 +114,40 @@ export declare function schema(configure: (env: Environment) => void, _blockRef:
148
114
  readonly variant?: "default" | "link" | "destructive" | "primary" | "ghost" | undefined;
149
115
  }[];
150
116
  } | undefined;
117
+ readonly query?: {
118
+ readonly style?: string | undefined;
119
+ readonly id?: string | undefined;
120
+ readonly initial?: {
121
+ readonly data: string;
122
+ readonly request?: string | undefined;
123
+ } | undefined;
124
+ readonly displayName?: string | undefined;
125
+ readonly kind: "shwfed.component.form";
126
+ readonly fields: readonly any[];
127
+ readonly layouts: readonly {
128
+ readonly name: string;
129
+ readonly layout: {
130
+ readonly style?: string | undefined;
131
+ readonly columns: number;
132
+ readonly gap?: number | undefined;
133
+ readonly rows?: number | undefined;
134
+ readonly placements: {
135
+ readonly [x: string]: {
136
+ readonly area: readonly [readonly [number, number], readonly [number, number]];
137
+ readonly v?: "stretch" | "center" | "end" | "start" | undefined;
138
+ readonly h?: "stretch" | "center" | "end" | "start" | undefined;
139
+ };
140
+ };
141
+ };
142
+ readonly media?: string | undefined;
143
+ }[];
144
+ readonly readonly?: string | undefined;
145
+ } | undefined;
146
+ readonly dataSource?: {
147
+ readonly data: string;
148
+ readonly total?: string | undefined;
149
+ readonly request?: string | undefined;
150
+ } | undefined;
151
151
  readonly bottomActions?: {
152
152
  readonly size: "default" | "sm" | "xs";
153
153
  readonly style?: string | undefined;
@@ -105,6 +105,8 @@ function handleClear() {
105
105
  </FieldLabel>
106
106
  <div
107
107
  v-if="effectiveReadonly"
108
+ data-slot="field-value"
109
+ data-value="true"
108
110
  class="min-h-9 py-1.5 text-sm"
109
111
  :class="draft ? 'text-zinc-700' : 'font-mono text-zinc-300 select-none'"
110
112
  >
@@ -87,6 +87,8 @@ const { isRequired, errors, warnings, visible } = useFieldValidation({
87
87
  </FieldLabel>
88
88
  <div
89
89
  v-if="effectiveReadonly"
90
+ data-slot="field-value"
91
+ data-value="true"
90
92
  class="min-h-9 py-1.5 text-sm whitespace-pre-wrap"
91
93
  :class="draft ? 'text-zinc-700' : 'font-mono text-zinc-300 select-none'"
92
94
  >
@@ -122,6 +122,8 @@ const { isRequired, errors, warnings, visible } = useFieldValidation({
122
122
  </FieldLabel>
123
123
  <div
124
124
  v-if="effectiveReadonly"
125
+ data-slot="field-value"
126
+ data-value="true"
125
127
  class="min-h-9 py-1.5 text-sm"
126
128
  :class="model ? 'text-zinc-700' : 'font-mono text-zinc-300 select-none'"
127
129
  >
@@ -152,6 +152,8 @@ const readonlyText = computed(() => {
152
152
  </FieldLabel>
153
153
  <div
154
154
  v-if="effectiveReadonly"
155
+ data-slot="field-value"
156
+ data-value="true"
155
157
  class="min-h-9 py-1.5 text-sm"
156
158
  :class="readonlyText ? 'text-zinc-700' : 'font-mono text-zinc-300 select-none'"
157
159
  >
@@ -122,6 +122,8 @@ const { isRequired, errors, warnings, visible } = useFieldValidation({
122
122
  </FieldLabel>
123
123
  <div
124
124
  v-if="effectiveReadonly"
125
+ data-slot="field-value"
126
+ data-value="true"
125
127
  class="min-h-9 py-1.5 text-sm"
126
128
  :class="model ? 'text-zinc-700' : 'font-mono text-zinc-300 select-none'"
127
129
  >
@@ -152,6 +152,8 @@ const readonlyText = computed(() => {
152
152
  </FieldLabel>
153
153
  <div
154
154
  v-if="effectiveReadonly"
155
+ data-slot="field-value"
156
+ data-value="true"
155
157
  class="min-h-9 py-1.5 text-sm"
156
158
  :class="readonlyText ? 'text-zinc-700' : 'font-mono text-zinc-300 select-none'"
157
159
  >
@@ -95,6 +95,8 @@ const { isRequired, errors, warnings, visible } = useFieldValidation({
95
95
  </FieldLabel>
96
96
  <div
97
97
  v-if="effectiveReadonly"
98
+ data-slot="field-value"
99
+ data-value="true"
98
100
  class="min-h-9 py-1.5 text-sm"
99
101
  :class="model ? 'text-zinc-700' : 'font-mono text-zinc-300 select-none'"
100
102
  >
@@ -118,6 +118,8 @@ const readonlyText = computed(() => {
118
118
  </FieldLabel>
119
119
  <div
120
120
  v-if="effectiveReadonly"
121
+ data-slot="field-value"
122
+ data-value="true"
121
123
  class="min-h-9 py-1.5 text-sm"
122
124
  :class="readonlyText ? 'text-zinc-700' : 'font-mono text-zinc-300 select-none'"
123
125
  >
@@ -1,13 +1,13 @@
1
1
  <script setup>
2
2
  import { Icon } from "@iconify/vue";
3
3
  import { Effect } from "effect";
4
- import { computed, nextTick } from "vue";
4
+ import { computed, nextTick, watch } from "vue";
5
5
  import { useI18n } from "vue-i18n";
6
6
  import { cel as _rawCel } from "../../../../../utils/cel";
7
7
  import { celBindings, injectCELContext } from "../../../../../utils/cel-context";
8
8
  import { getLocalizedText } from "../../../../../share/locale";
9
9
  import { interpolateMarkdown } from "../../../../table/utils/runtime";
10
- import { formatNumberDisplay } from "../../../../../share/number-format";
10
+ import { formatNumberDisplay, roundToPrecision } from "../../../../../share/number-format";
11
11
  import { Field, FieldLabel, FieldMessages } from "../../../../ui/field";
12
12
  import { InputGroup, InputGroupAddon, InputGroupButton, InputGroupNumberField } from "../../../../ui/input-group";
13
13
  import { Markdown } from "../../../../ui/markdown";
@@ -16,7 +16,7 @@ import { DEFAULT_FIELD_ORIENTATION } from "../../../utils/common";
16
16
  import { useFieldValue } from "../../../utils/field-value";
17
17
  import { useFieldValidation } from "../../../utils/validation";
18
18
  import { useFormReadonly } from "../../../utils/readonly";
19
- import { useFormScope } from "../../../utils/state";
19
+ import { useFormScope, useFormState } from "../../../utils/state";
20
20
  defineOptions({ name: "ShwfedNumberFieldRuntime" });
21
21
  const props = defineProps({
22
22
  fieldId: { type: String, required: true },
@@ -86,22 +86,29 @@ const { draft, commit } = useFieldValue({
86
86
  return props.config.valueAsString ? String(next) : next;
87
87
  }
88
88
  });
89
+ const bag = useFormState();
90
+ watch(
91
+ () => [props.config.binding, props.config.precision, props.config.roundingMode, props.config.valueAsString],
92
+ ([binding, precision, roundingMode, valueAsString], _prev, onCleanup) => {
93
+ if (binding == null || precision === void 0) return;
94
+ const mode = roundingMode ?? "round";
95
+ const unregister = bag.registerCoercion(binding, (value) => {
96
+ let n;
97
+ if (typeof value === "number" && Number.isFinite(value)) n = value;
98
+ else if (typeof value === "string" && value.length > 0 && Number.isFinite(Number(value))) n = Number(value);
99
+ if (n === void 0) return value;
100
+ const rounded = roundToPrecision(n, precision, mode);
101
+ return valueAsString ? String(rounded) : rounded;
102
+ });
103
+ onCleanup(unregister);
104
+ },
105
+ { immediate: true }
106
+ );
89
107
  const readonlyDisplay = computed(() => formatNumberDisplay(draft.value, {
90
108
  displayMode: props.config.displayMode,
91
109
  precision: props.config.precision,
92
110
  roundingMode: props.config.roundingMode
93
111
  }));
94
- function applyRounding(n, precision, mode) {
95
- const factor = 10 ** precision;
96
- switch (mode) {
97
- case "floor":
98
- return Math.floor(n * factor) / factor;
99
- case "ceil":
100
- return Math.ceil(n * factor) / factor;
101
- default:
102
- return Math.round(n * factor) / factor;
103
- }
104
- }
105
112
  const addonConfig = computed(() => {
106
113
  const addon = props.config.addon;
107
114
  if (!addon || addon.items.length === 0) return null;
@@ -125,7 +132,7 @@ async function onBlur() {
125
132
  if (precision !== void 0) {
126
133
  const current = draft.value;
127
134
  if (current !== void 0) {
128
- const rounded = applyRounding(current, precision, props.config.roundingMode ?? "round");
135
+ const rounded = roundToPrecision(current, precision, props.config.roundingMode ?? "round");
129
136
  if (rounded !== current) draft.value = rounded;
130
137
  }
131
138
  }
@@ -160,6 +167,8 @@ async function onBlur() {
160
167
  </FieldLabel>
161
168
  <div
162
169
  v-if="effectiveReadonly"
170
+ data-slot="field-value"
171
+ data-value="true"
163
172
  class="min-h-9 py-1.5 text-sm"
164
173
  :class="[
165
174
  draft != null ? 'text-zinc-700' : 'font-mono text-zinc-300 select-none',
@@ -6,6 +6,7 @@ import { useI18n } from "vue-i18n";
6
6
  import { cel as _rawCel } from "../../../../../utils/cel";
7
7
  import { celBindings, injectCELContext } from "../../../../../utils/cel-context";
8
8
  import { getLocalizedText } from "../../../../../share/locale";
9
+ import { roundToPrecision } from "../../../../../share/number-format";
9
10
  import { interpolateMarkdown } from "../../../../table/utils/runtime";
10
11
  import { Field, FieldLabel, FieldMessages } from "../../../../ui/field";
11
12
  import { InputGroup, InputGroupAddon, InputGroupButton, InputGroupNumberField } from "../../../../ui/input-group";
@@ -21,7 +22,8 @@ const props = defineProps({
21
22
  config: { type: null, required: true }
22
23
  });
23
24
  const { locale } = useI18n();
24
- const { getAt, setAt } = useFormState();
25
+ const bag = useFormState();
26
+ const { getAt, setAt } = bag;
25
27
  const formScope = useFormScope();
26
28
  const inherited = injectCELContext();
27
29
  const $cel = (expression, context) => _rawCel(expression, { ...celBindings(inherited), ...context });
@@ -158,17 +160,6 @@ const endMin = computed(() => {
158
160
  if (typeof s === "number") return s;
159
161
  return m;
160
162
  });
161
- function applyRounding(n, precision, mode) {
162
- const factor = 10 ** precision;
163
- switch (mode) {
164
- case "floor":
165
- return Math.floor(n * factor) / factor;
166
- case "ceil":
167
- return Math.ceil(n * factor) / factor;
168
- default:
169
- return Math.round(n * factor) / factor;
170
- }
171
- }
172
163
  async function onBlurSide(side) {
173
164
  await nextTick();
174
165
  const precision = props.config.precision;
@@ -176,12 +167,42 @@ async function onBlurSide(side) {
176
167
  const ref_ = side === "start" ? localStart : localEnd;
177
168
  const current = ref_.value;
178
169
  if (current !== void 0) {
179
- const rounded = applyRounding(current, precision, props.config.roundingMode ?? "round");
170
+ const rounded = roundToPrecision(current, precision, props.config.roundingMode ?? "round");
180
171
  if (rounded !== current) ref_.value = rounded;
181
172
  }
182
173
  }
183
174
  commit();
184
175
  }
176
+ function roundScalar(precision, mode, value) {
177
+ let n;
178
+ if (typeof value === "number" && Number.isFinite(value)) n = value;
179
+ else if (typeof value === "string" && value.length > 0 && Number.isFinite(Number(value))) n = Number(value);
180
+ if (n === void 0) return value;
181
+ const rounded = roundToPrecision(n, precision, mode);
182
+ return props.config.valueAsString ? String(rounded) : rounded;
183
+ }
184
+ watch(
185
+ () => [props.config.binding, props.config.precision, props.config.roundingMode, props.config.valueAsString],
186
+ ([binding, precision, roundingMode], _prev, onCleanup) => {
187
+ if (binding == null || precision === void 0) return;
188
+ const mode = roundingMode ?? "round";
189
+ const unregisters = [];
190
+ if (typeof binding === "string") {
191
+ unregisters.push(bag.registerCoercion(
192
+ binding,
193
+ (value) => Array.isArray(value) && value.length === 2 ? [roundScalar(precision, mode, value[0]), roundScalar(precision, mode, value[1])] : value
194
+ ));
195
+ } else {
196
+ for (const path of binding) {
197
+ unregisters.push(bag.registerCoercion(path, (value) => roundScalar(precision, mode, value)));
198
+ }
199
+ }
200
+ onCleanup(() => {
201
+ for (const off of unregisters) off();
202
+ });
203
+ },
204
+ { immediate: true }
205
+ );
185
206
  const showClear = computed(
186
207
  () => !isDisabled.value && (localStart.value !== void 0 || localEnd.value !== void 0)
187
208
  );
@@ -224,6 +245,8 @@ const readonlyText = computed(() => {
224
245
  </FieldLabel>
225
246
  <div
226
247
  v-if="effectiveReadonly"
248
+ data-slot="field-value"
249
+ data-value="true"
227
250
  class="min-h-9 py-1.5 text-sm"
228
251
  :class="readonlyText ? 'text-zinc-700' : 'font-mono text-zinc-300 select-none'"
229
252
  >
@@ -16,6 +16,7 @@ import { Field, FieldLabel, FieldMessages } from "../../../../ui/field";
16
16
  import { InputGroupButton } from "../../../../ui/input-group";
17
17
  import { Markdown } from "../../../../ui/markdown";
18
18
  import { useFieldValidation } from "../../../utils/validation";
19
+ import { useFormReadonly } from "../../../utils/readonly";
19
20
  import { SELF_BINDING, useFormScope, useFormState } from "../../../utils/state";
20
21
  import Row from "./row.vue";
21
22
  defineOptions({ name: "ShwfedListFieldRuntime" });
@@ -54,6 +55,7 @@ const { isRequired, errors, warnings, visible } = useFieldValidation({
54
55
  locale: () => locale.value
55
56
  });
56
57
  const isDisabled = computed(() => evalBool(props.config.disabled, "disabled"));
58
+ const isReadonly = useFormReadonly();
57
59
  const reorderable = computed(() => props.config.reorderable ?? true);
58
60
  const addable = computed(() => props.config.addable ?? true);
59
61
  const isScalarRow = computed(() => {
@@ -242,7 +244,7 @@ function rowConfig(i) {
242
244
  >
243
245
  <div class="flex items-center gap-2">
244
246
  <Icon
245
- v-if="reorderable"
247
+ v-if="reorderable && !isReadonly"
246
248
  icon="fluent:re-order-dots-vertical-20-regular"
247
249
  class="drag-handle size-4 shrink-0 text-zinc-400"
248
250
  :class="canReorder ? 'cursor-grab' : 'cursor-not-allowed opacity-50'"
@@ -286,6 +288,7 @@ function rowConfig(i) {
286
288
  </div>
287
289
 
288
290
  <InputGroupButton
291
+ v-if="!isReadonly"
289
292
  variant="destructive"
290
293
  size="icon-xs"
291
294
  class="shrink-0"
@@ -314,7 +317,7 @@ function rowConfig(i) {
314
317
  </div>
315
318
 
316
319
  <Button
317
- v-if="addable"
320
+ v-if="addable && !isReadonly"
318
321
  type="button"
319
322
  :disabled="!canAdd"
320
323
  data-slot="list-add"
@@ -122,6 +122,8 @@ const model = computed({
122
122
  </FieldLabel>
123
123
  <div
124
124
  v-if="effectiveReadonly"
125
+ data-slot="field-value"
126
+ data-value="true"
125
127
  class="min-h-9 py-1.5 text-sm"
126
128
  :class="model ? 'text-zinc-700' : 'font-mono text-zinc-300 select-none'"
127
129
  >
@@ -152,6 +152,8 @@ const readonlyText = computed(() => {
152
152
  </FieldLabel>
153
153
  <div
154
154
  v-if="effectiveReadonly"
155
+ data-slot="field-value"
156
+ data-value="true"
155
157
  class="min-h-9 py-1.5 text-sm"
156
158
  :class="readonlyText ? 'text-zinc-700' : 'font-mono text-zinc-300 select-none'"
157
159
  >
@@ -382,6 +382,8 @@ const hoveredTooltip = computed(() => {
382
382
 
383
383
  <div
384
384
  v-if="effectiveReadonly"
385
+ data-slot="field-value"
386
+ data-value="true"
385
387
  class="min-h-9 py-1.5 text-sm"
386
388
  :class="triggerLabel ? 'text-zinc-700' : 'font-mono text-zinc-300 select-none'"
387
389
  >
@@ -370,6 +370,8 @@ const hoveredTooltip = computed(() => {
370
370
 
371
371
  <div
372
372
  v-if="effectiveReadonly"
373
+ data-slot="field-value"
374
+ data-value="true"
373
375
  class="min-h-9 py-1.5 text-sm"
374
376
  :class="triggerLabel ? 'text-zinc-700' : 'font-mono text-zinc-300 select-none'"
375
377
  >
@@ -397,29 +397,33 @@ const readonlyText = computed(() => {
397
397
  :class="isDisabled ? 'pointer-events-none opacity-60' : void 0"
398
398
  >
399
399
  <template #node="{ node, selected, indeterminate }">
400
- <span class="flex items-start gap-2">
401
- <!-- Presentational checkbox: mirrors the row's selection state but
402
- never owns it. `pointer-events-none` + `tabindex=-1` keep the
403
- existing row-click / keyboard toggle in `<UiTree>` the single
404
- source of truth the control is a visual reflection only. -->
405
- <Checkbox
406
- :model-value="indeterminate ? 'indeterminate' : selected"
407
- class="pointer-events-none mt-0.5 shrink-0"
408
- tabindex="-1"
409
- aria-hidden="true"
410
- />
411
- <span class="flex flex-col gap-0.5">
400
+ <span class="flex flex-col gap-0.5">
401
+ <!-- Checkbox shares a centered row with the label so it aligns to
402
+ the label's line box (not the top of the label+tooltip stack).
403
+ The tooltip drops below, indented `pl-6` (checkbox `size-4` +
404
+ `gap-2`) to hang under the label text. -->
405
+ <span class="flex items-center gap-2">
406
+ <!-- Presentational checkbox: mirrors the row's selection state but
407
+ never owns it. `pointer-events-none` + `tabindex=-1` keep the
408
+ existing row-click / keyboard toggle in `<UiTree>` the single
409
+ source of truth — the control is a visual reflection only. -->
410
+ <Checkbox
411
+ :model-value="indeterminate ? 'indeterminate' : selected"
412
+ class="pointer-events-none shrink-0"
413
+ tabindex="-1"
414
+ aria-hidden="true"
415
+ />
412
416
  <Markdown
413
417
  :source="nodeLabelText(node)"
414
418
  class="prose prose-sm prose-zinc text-zinc-700"
415
419
  />
416
- <Markdown
417
- v-if="nodeTooltipText(node)"
418
- :source="nodeTooltipText(node)"
419
- block
420
- class="prose prose-xs prose-zinc text-zinc-400"
421
- />
422
420
  </span>
421
+ <Markdown
422
+ v-if="nodeTooltipText(node)"
423
+ :source="nodeTooltipText(node)"
424
+ block
425
+ class="prose prose-xs prose-zinc text-zinc-400 pl-6"
426
+ />
423
427
  </span>
424
428
  </template>
425
429
 
@@ -375,7 +375,15 @@ async function onDownloadTemplate(template) {
375
375
  :data-readonly="isReadonly || void 0"
376
376
  >
377
377
  <div
378
- v-if="entries.length === 0"
378
+ v-if="entries.length === 0 && isReadonly"
379
+ class="min-h-9 py-1.5 text-sm font-mono text-zinc-300 select-none"
380
+ data-slot="upload-empty"
381
+ >
382
+ {{ "-" }}
383
+ </div>
384
+
385
+ <div
386
+ v-else-if="entries.length === 0"
379
387
  role="button"
380
388
  :tabindex="canAdd ? 0 : -1"
381
389
  data-slot="upload-zone"