77 <div v-if =" column.numberPrefix" class =" prefix" >
88 {{ column.numberPrefix }}
99 </div >
10- <input v-model =" localValue"
11- type =" number"
12- :min =" column.numberMin"
13- :max =" column.numberMax"
14- :readonly =" column.viewColumnInformation?.readonly"
15- :step =" getStep" >
10+ <div class =" number-input" >
11+ <input v-model =" localValue"
12+ type =" number"
13+ class =" number-input__field"
14+ :class =" { 'number-input__field--error': hasRangeError }"
15+ :min =" column.numberMin"
16+ :max =" column.numberMax"
17+ :readonly =" column.viewColumnInformation?.readonly"
18+ :step =" getStep"
19+ :aria-invalid =" hasRangeError"
20+ @blur =" formatValue"
21+ @keyup.enter =" formatValue" >
22+ <p v-if =" hasRangeError" class =" number-input__hint" role =" alert" >
23+ {{ rangeHintText }}
24+ </p >
25+ </div >
1626 <div v-if =" column.numberSuffix" class =" suffix" >
1727 {{ column.numberSuffix }}
1828 </div >
2131
2232<script >
2333import RowFormWrapper from ' ./RowFormWrapper.vue'
34+ import { translate as t } from ' @nextcloud/l10n'
2435import rowHelper from ' ../../../../components/ncTable/mixins/rowHelper.js'
2536export default {
2637
@@ -49,6 +60,37 @@ export default {
4960 return ' any'
5061 }
5162 },
63+ hasMin () {
64+ return this .column ? .numberMin !== null && this .column ? .numberMin !== undefined
65+ },
66+ hasMax () {
67+ return this .column ? .numberMax !== null && this .column ? .numberMax !== undefined
68+ },
69+ hasRangeError () {
70+ const value = this .parseValue (this .localValue )
71+ if (value === null ) {
72+ return false
73+ }
74+ if (this .hasMin && value < this .column .numberMin ) {
75+ return true
76+ }
77+ if (this .hasMax && value > this .column .numberMax ) {
78+ return true
79+ }
80+ return false
81+ },
82+ rangeHintText () {
83+ const min = this .column ? .numberMin
84+ const max = this .column ? .numberMax
85+ if (this .hasMin && this .hasMax ) {
86+ return t (' tables' , ' Enter a value between {min} and {max}.' , { min, max })
87+ } else if (this .hasMin ) {
88+ return t (' tables' , ' Enter a value of {min} or more.' , { min })
89+ } else if (this .hasMax ) {
90+ return t (' tables' , ' Enter a value of {max} or less.' , { max })
91+ }
92+ return ' '
93+ },
5294 localValue: {
5395 get () {
5496 if (this .value !== null ) {
@@ -62,16 +104,13 @@ export default {
62104 }
63105 }
64106 },
65- set (v ) { this .$emit (' update:value' , this .parseValue (v)) },
107+ set (v ) {
108+ this .$emit (' update:value' , v === ' ' ? null : v)
109+ },
66110 },
67111 },
68112
69113 watch: {
70- localValue () {
71- const value = this .parseValue (this .localValue )
72- this .localValue = value
73- this .$emit (' update:value' , value)
74- },
75114 value () {
76115 this .localValue = this .value
77116 },
@@ -82,6 +121,15 @@ export default {
82121 },
83122
84123 methods: {
124+ t,
125+ // Normalise the input (decimal separator + rounding) without clamping.
126+ // Out-of-range values are surfaced via hasRangeError instead of being
127+ // silently replaced by the min/max value.
128+ formatValue () {
129+ const parsedValue = this .parseValue (this .localValue )
130+ this .localValue = parsedValue
131+ this .$emit (' update:value' , parsedValue)
132+ },
85133 parseValue (inputValue ) {
86134 if (inputValue === null || inputValue === ' ' ) {
87135 return null
@@ -92,15 +140,11 @@ export default {
92140 } else {
93141 parsedValue = inputValue
94142 }
95- const roundedValue = parsedValue .toFixed (this .column ? .numberDecimals )
96- let value = parseFloat (roundedValue)
97- if ((this .column ? .numberMin !== null && this .column ? .numberMin !== undefined ) && value < this .column ? .numberMin ) {
98- value = this .column .numberMin
99- }
100- if ((this .column ? .numberMax !== null && this .column ? .numberMax !== undefined ) && value > this .column ? .numberMax ) {
101- value = this .column .numberMax
143+ if (isNaN (parsedValue)) {
144+ return null
102145 }
103- return value
146+ const roundedValue = parsedValue .toFixed (this .column ? .numberDecimals )
147+ return parseFloat (roundedValue)
104148 },
105149 },
106150}
@@ -115,4 +159,25 @@ export default {
115159 padding- inline- start: calc (var (-- default- grid- baseline) * 2 );
116160}
117161
162+ .number - input {
163+ display: flex;
164+ flex- direction: column;
165+ flex: 1 ;
166+ min- width: 0 ;
167+
168+ & __field {
169+ width: 100 % ;
170+
171+ & -- error {
172+ border- color: var (-- color- error) ! important;
173+ }
174+ }
175+
176+ & __hint {
177+ margin- block- start: calc (var (-- default- grid- baseline) * 1 );
178+ color: var (-- color- error);
179+ font- size: 0 .9em ;
180+ }
181+ }
182+
118183< / style>
0 commit comments