@veritree/ui 0.82.0-0 → 0.82.0-2

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.82.0-0",
3
+ "version": "0.82.0-2",
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: null,
58
+ key: null,
59
+ };
60
+ },
61
+
35
62
  computed: {
36
63
  valueComputed: {
37
64
  get() {
@@ -48,12 +75,45 @@ 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 !== 'input') {
86
+ console.log(1);
87
+ options = {
88
+ minimumFractionDigits: 2,
89
+ maximumFractionDigits: 2,
90
+ };
91
+ } else {
92
+ options = {};
93
+ }
94
+
95
+ return Number(this.value).toLocaleString(this.locale, options);
52
96
  },
53
97
  set(value) {
98
+ // value without formatting that will be
54
99
  this.valueComputed = value;
55
100
  },
56
101
  },
102
+
103
+ currencySymbol() {
104
+ return getCurrencySymbol({
105
+ locale: this.locale,
106
+ currency: this.currency,
107
+ });
108
+ },
109
+
110
+ inputNumberClassComputed() {
111
+ if (this.format === 'currency') {
112
+ return [this.classComputed, 'flex justify-start'];
113
+ }
114
+
115
+ return this.classComputed;
116
+ },
57
117
  },
58
118
 
59
119
  methods: {
@@ -68,6 +128,10 @@ export default {
68
128
  return null; // or any other appropriate value or behavior
69
129
  }
70
130
 
131
+ if (this.format === 'currency') {
132
+ return parseFloat(number.replace(/,/g, ''));
133
+ }
134
+
71
135
  return parseInt(number.replace(/,/g, ''), 10);
72
136
  },
73
137
 
@@ -91,12 +155,42 @@ export default {
91
155
  },
92
156
 
93
157
  onInput(e) {
158
+ this.event = 'input';
94
159
  this.valueComputed = e.target.value;
95
160
  },
96
161
 
97
- onBlur() {
162
+ onBlur(e) {
163
+ this.event = 'blur';
164
+
165
+ // only set valueComputed if it's greater than 0
166
+ if (!this.valueComputed && this.valueComputed > 0) {
167
+ this.valueComputed = e.target.value;
168
+ }
169
+
98
170
  this.$emit('blur');
99
171
  },
172
+
173
+ /**
174
+ * Handles the keydown event to restrict input to valid characters.
175
+ *
176
+ * This function prevents the default action for any key presses that do not correspond
177
+ * to a digit (0-9), a comma, or a period. It also allows common navigation and editing
178
+ * keys such as Backspace, Delete, Arrow keys, and Tab.
179
+ *
180
+ * @param {Event} e - The keydown event object.
181
+ */
182
+ onKeyDown(e) {
183
+ if (
184
+ !/^[0-9.,]$/.test(e.key) &&
185
+ !['Backspace', 'Delete', 'ArrowLeft', 'ArrowRight', 'Tab'].includes(
186
+ e.key,
187
+ )
188
+ ) {
189
+ e.preventDefault();
190
+ }
191
+
192
+ this.key = e.key;
193
+ },
100
194
  },
101
195
  };
102
196
  </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
+ };