@veritree/ui 0.81.0 → 0.82.0-1

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.
@@ -73,7 +73,7 @@ export const formControlStyleMixin = {
73
73
  return [
74
74
  this.headless
75
75
  ? `${this.name}`
76
- : 'leading-0 bg-white disabled:bg-gray-200 flex w-full max-w-full relative appearance-none placeholder:font-light placeholder:text-gray-500 items-center justify-between rounded border border-solid px-3 py-2 font-inherit text-inherit file:hidden focus:border-gray-600 focus:placeholder:text-gray-400 disabled:text-gray-500',
76
+ : 'leading-0 bg-white has-[:disabled]:bg-gray-200 disabled:bg-gray-200 flex w-full max-w-full relative appearance-none placeholder:font-light placeholder:text-gray-500 items-center justify-between rounded border border-solid px-3 py-2 font-inherit text-inherit file:hidden focus-within:border-gray-600 focus:border-gray-600 focus-within:placeholder:text-gray-400 focus:placeholder:text-gray-400 has-[:disabled]:text-gray-500 disabled:text-gray-500',
77
77
  // variant styles
78
78
  this.headless
79
79
  ? `${this.name}--${this.variant}`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@veritree/ui",
3
- "version": "0.81.0",
3
+ "version": "0.82.0-1",
4
4
  "description": "veritree ui library",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -1,12 +1,18 @@
1
1
  <template>
2
- <input
3
- v-model="valueFormatted"
4
- :class="classComputed"
5
- :disabled="disabled"
6
- @input="onInput"
7
- @change="onChange"
8
- @blur="onBlur"
9
- />
2
+ <div :class="inputNumberClassComputed">
3
+ <span v-if="format === 'currency'" class="grow-0 mr-1">{{
4
+ currencySymbol
5
+ }}</span>
6
+ <input
7
+ v-model="valueFormatted"
8
+ :disabled="disabled"
9
+ class="w-full"
10
+ @input="onInput"
11
+ @change="onChange"
12
+ @blur="onBlur"
13
+ @keydown="onKeyDown"
14
+ />
15
+ </div>
10
16
  </template>
11
17
 
12
18
  <script>
@@ -14,6 +20,7 @@ import {
14
20
  formControlMixin,
15
21
  formControlStyleMixin,
16
22
  } from '../../../mixins/form-control';
23
+ import { getCurrencySymbol } from '../../helpers/currency';
17
24
 
18
25
  export default {
19
26
  name: 'VTInputNumber',
@@ -26,12 +33,32 @@ export default {
26
33
  },
27
34
 
28
35
  props: {
36
+ currency: {
37
+ type: String,
38
+ default: 'USD',
39
+ },
40
+ locale: {
41
+ type: String,
42
+ default: 'en-US',
43
+ },
44
+ // different type of number input (e.g. 'integer', 'decimal', 'currency')
45
+ format: {
46
+ type: String,
47
+ default: 'integer',
48
+ },
29
49
  value: {
30
50
  type: [String, Number, Object, Array, Date],
31
51
  default: null,
32
52
  },
33
53
  },
34
54
 
55
+ data() {
56
+ return {
57
+ event: 'input',
58
+ key: null,
59
+ };
60
+ },
61
+
35
62
  computed: {
36
63
  valueComputed: {
37
64
  get() {
@@ -48,12 +75,44 @@ export default {
48
75
 
49
76
  valueFormatted: {
50
77
  get() {
51
- return Number(this.value).toLocaleString();
78
+ if (this.value === 0 && this.event === 'blur') {
79
+ return null;
80
+ }
81
+
82
+ let options = {};
83
+
84
+ // only add the fraction digits on blur because on input it won't behave nicely
85
+ if (this.format === 'currency' && this.event === 'blur') {
86
+ options = {
87
+ minimumFractionDigits: 2,
88
+ maximumFractionDigits: 2,
89
+ };
90
+ } else {
91
+ options = {};
92
+ }
93
+
94
+ return Number(this.value).toLocaleString(this.locale, options);
52
95
  },
53
96
  set(value) {
97
+ // value without formatting that will be
54
98
  this.valueComputed = value;
55
99
  },
56
100
  },
101
+
102
+ currencySymbol() {
103
+ return getCurrencySymbol({
104
+ locale: this.locale,
105
+ currency: this.currency,
106
+ });
107
+ },
108
+
109
+ inputNumberClassComputed() {
110
+ if (this.format === 'currency') {
111
+ return [this.classComputed, 'flex justify-start'];
112
+ }
113
+
114
+ return this.classComputed;
115
+ },
57
116
  },
58
117
 
59
118
  methods: {
@@ -68,6 +127,10 @@ export default {
68
127
  return null; // or any other appropriate value or behavior
69
128
  }
70
129
 
130
+ if (this.format === 'currency') {
131
+ return parseFloat(number.replace(/,/g, ''));
132
+ }
133
+
71
134
  return parseInt(number.replace(/,/g, ''), 10);
72
135
  },
73
136
 
@@ -91,12 +154,42 @@ export default {
91
154
  },
92
155
 
93
156
  onInput(e) {
157
+ this.event = 'input';
94
158
  this.valueComputed = e.target.value;
95
159
  },
96
160
 
97
- onBlur() {
161
+ onBlur(e) {
162
+ this.event = 'blur';
163
+
164
+ // only set valueComputed if it's greater than 0
165
+ if (!this.valueComputed && this.valueComputed > 0) {
166
+ this.valueComputed = e.target.value;
167
+ }
168
+
98
169
  this.$emit('blur');
99
170
  },
171
+
172
+ /**
173
+ * Handles the keydown event to restrict input to valid characters.
174
+ *
175
+ * This function prevents the default action for any key presses that do not correspond
176
+ * to a digit (0-9), a comma, or a period. It also allows common navigation and editing
177
+ * keys such as Backspace, Delete, Arrow keys, and Tab.
178
+ *
179
+ * @param {Event} e - The keydown event object.
180
+ */
181
+ onKeyDown(e) {
182
+ if (
183
+ !/^[0-9.,]$/.test(e.key) &&
184
+ !['Backspace', 'Delete', 'ArrowLeft', 'ArrowRight', 'Tab'].includes(
185
+ e.key,
186
+ )
187
+ ) {
188
+ e.preventDefault();
189
+ }
190
+
191
+ this.key = e.key;
192
+ },
100
193
  },
101
194
  };
102
195
  </script>
@@ -0,0 +1,21 @@
1
+ export const currencyFormatter = ({ locale = 'en-US', currency = 'USD' }) => {
2
+ return Intl.NumberFormat(locale, {
3
+ style: 'currency',
4
+ currency,
5
+ });
6
+ };
7
+
8
+ export const getCurrencySymbol = (options) => {
9
+ const formatter = currencyFormatter(options);
10
+ const parts = formatter.formatToParts(0); // can be any number
11
+ const currency = parts.find((part) => part.type === 'currency');
12
+ const symbol = currency.value;
13
+
14
+ return removeLetters(symbol);
15
+ };
16
+
17
+ // Remove all letters (for example, change CA$ to $)
18
+ const removeLetters = (value) => {
19
+ const lettersRegex = /[A-Za-z]/g;
20
+ return value.replaceAll(lettersRegex, '');
21
+ };