@per-diem-calculator/vanilla 1.0.0
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.
- package/.prettierrc +17 -0
- package/LICENSE +21 -0
- package/README.md +1 -0
- package/eslint.config.js +29 -0
- package/index.html +11 -0
- package/package.json +49 -0
- package/public/output.css +2503 -0
- package/src/css/_styles.css +8 -0
- package/src/css/colors.css +45 -0
- package/src/css/fonts.css +9 -0
- package/src/css/rows/_heights.css +6 -0
- package/src/css/rows/_index.css +15 -0
- package/src/css/rows/add.css +18 -0
- package/src/css/rows/animate-btns.css +18 -0
- package/src/css/rows/animate-row-close.css +18 -0
- package/src/css/rows/animate-row-open.css +14 -0
- package/src/css/rows/animate-row-other.css +5 -0
- package/src/css/rows/btn-add-row.css +41 -0
- package/src/css/rows/btn-delete.css +22 -0
- package/src/css/rows/btn-expenses-calculate.css +22 -0
- package/src/css/rows/btn-expenses-category.css +22 -0
- package/src/css/rows/delete.css +10 -0
- package/src/css/rows/details.css +22 -0
- package/src/css/rows/expense.css +18 -0
- package/src/css/rows/location.css +34 -0
- package/src/css/rows/summary.css +22 -0
- package/src/css/tom-select/defaults.css +530 -0
- package/src/css/tom-select/overrides.css +55 -0
- package/src/css/tw-shadow-props.css +50 -0
- package/src/index.ts +1 -0
- package/src/ts/components/Button/Button.ts +50 -0
- package/src/ts/components/Button/template.html +34 -0
- package/src/ts/components/ExpenseRow/ExpenseRow.ts +397 -0
- package/src/ts/components/ExpenseRow/template.html +260 -0
- package/src/ts/components/Label/Label.ts +45 -0
- package/src/ts/components/Label/template.html +1 -0
- package/src/ts/components/LocationCategory/LocationCategory.ts +226 -0
- package/src/ts/components/LocationCategory/template.html +520 -0
- package/src/ts/components/LocationDate/LocationDate.ts +366 -0
- package/src/ts/components/LocationDate/template.html +27 -0
- package/src/ts/components/LocationSelect/LocationSelect.ts +299 -0
- package/src/ts/components/LocationSelect/template.html +45 -0
- package/src/ts/components/index.ts +6 -0
- package/src/ts/controller.ts +193 -0
- package/src/ts/model.ts +163 -0
- package/src/ts/types/config.ts +22 -0
- package/src/ts/types/dates.ts +82 -0
- package/src/ts/types/expenses.ts +73 -0
- package/src/ts/types/locations.ts +25 -0
- package/src/ts/utils/config/configDefault.ts +13 -0
- package/src/ts/utils/config/index.ts +12 -0
- package/src/ts/utils/config/numbers.ts +24 -0
- package/src/ts/utils/config/sanitizeConfig.ts +39 -0
- package/src/ts/utils/dates/INPUT_DATE_MINMAX.ts +5 -0
- package/src/ts/utils/dates/YEAR_REGEX.ts +4 -0
- package/src/ts/utils/dates/getDateSlice.ts +54 -0
- package/src/ts/utils/dates/getValidAPIYear.ts +17 -0
- package/src/ts/utils/dates/index.ts +19 -0
- package/src/ts/utils/dates/isDateRaw.ts +90 -0
- package/src/ts/utils/dates/isShortMonth.ts +24 -0
- package/src/ts/utils/dates/isYYYY.ts +10 -0
- package/src/ts/utils/dates/offsetDateString.ts +17 -0
- package/src/ts/utils/expenses/INTL_MIE_RATES.ts +2125 -0
- package/src/ts/utils/expenses/createExpenseObjs.ts +35 -0
- package/src/ts/utils/expenses/getLodgingRateDomestic.ts +73 -0
- package/src/ts/utils/expenses/getLodgingRateIntl.ts +119 -0
- package/src/ts/utils/expenses/getMieRates.ts +84 -0
- package/src/ts/utils/expenses/index.ts +5 -0
- package/src/ts/utils/expenses/parseIntlLodgingRates.ts +124 -0
- package/src/ts/utils/expenses/returnValidStateExpense.ts +46 -0
- package/src/ts/utils/fetch/fetchJsonGSA.ts +29 -0
- package/src/ts/utils/fetch/fetchXmlDOD.ts +38 -0
- package/src/ts/utils/fetch/index.ts +3 -0
- package/src/ts/utils/fetch/memoize.ts +46 -0
- package/src/ts/utils/fetch/parseXml.ts +19 -0
- package/src/ts/utils/locations/getCitiesDomestic.ts +48 -0
- package/src/ts/utils/locations/getCitiesIntl.ts +63 -0
- package/src/ts/utils/locations/getCountriesDomestic.ts +237 -0
- package/src/ts/utils/locations/getCountriesIntl.ts +34 -0
- package/src/ts/utils/locations/index.ts +6 -0
- package/src/ts/utils/locations/keepUniqueLocations.ts +12 -0
- package/src/ts/utils/locations/locationKeys.ts +10 -0
- package/src/ts/utils/locations/returnValidStateLocation.ts +13 -0
- package/src/ts/utils/locations/sortLocations.ts +19 -0
- package/src/ts/utils/misc/USD.ts +4 -0
- package/src/ts/utils/misc/debounce.ts +22 -0
- package/src/ts/utils/misc/handlePointerDown.ts +3 -0
- package/src/ts/utils/misc/handlePointerUp.ts +22 -0
- package/src/ts/utils/misc/inPrimitiveType.ts +4 -0
- package/src/ts/utils/misc/index.ts +6 -0
- package/src/ts/utils/misc/wait.ts +4 -0
- package/src/ts/utils/styles/applyStyles.ts +19 -0
- package/src/ts/utils/styles/highlightInput.ts +15 -0
- package/src/ts/utils/styles/index.ts +3 -0
- package/src/ts/utils/styles/removeStyles.ts +14 -0
- package/src/ts/views/Expense/Expense.ts +465 -0
- package/src/ts/views/Expense/template.html +176 -0
- package/src/ts/views/Location/Location.ts +763 -0
- package/src/ts/views/Location/template-row.html +146 -0
- package/src/ts/views/Location/template.html +130 -0
- package/src/ts/views/index.ts +2 -0
- package/tsconfig.json +27 -0
- package/vite.config.ts +12 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// Types
|
|
2
|
+
import type { Location } from '../../types/locations';
|
|
3
|
+
import type { DateRaw } from '../../types/dates';
|
|
4
|
+
|
|
5
|
+
// Utils
|
|
6
|
+
import { fetchXmlDOD, parseXml } from '../fetch';
|
|
7
|
+
import { getValidAPIYear } from '../dates';
|
|
8
|
+
import { sortLocations } from './sortLocations';
|
|
9
|
+
import { keepUniqueLocations } from './keepUniqueLocations';
|
|
10
|
+
|
|
11
|
+
const getCities = (data: string, country: string): Element[] => {
|
|
12
|
+
return parseXml(
|
|
13
|
+
data,
|
|
14
|
+
`//record[country_name[text()="${country.toUpperCase()}"]]`,
|
|
15
|
+
) as Element[];
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const createLocations = (
|
|
19
|
+
data: Element[],
|
|
20
|
+
date: DateRaw,
|
|
21
|
+
country: string,
|
|
22
|
+
): Location[] => {
|
|
23
|
+
return data.reduce((result: Location[], record) => {
|
|
24
|
+
const city = record.querySelector('location_name')?.textContent;
|
|
25
|
+
const expDateText = record.querySelector('exp_date')?.textContent;
|
|
26
|
+
const effDateText = record.querySelector('eff_date')?.textContent;
|
|
27
|
+
if (!city || !expDateText || !effDateText) return result; // City not found, move to next
|
|
28
|
+
|
|
29
|
+
const expDate = new Date(
|
|
30
|
+
`${expDateText.split('/')[2]}-${expDateText.split('/')[0]}-${expDateText.split('/')[1]}`,
|
|
31
|
+
);
|
|
32
|
+
const effDate = new Date(
|
|
33
|
+
`${effDateText.split('/')[2]}-${effDateText.split('/')[0]}-${effDateText.split('/')[1]}`,
|
|
34
|
+
);
|
|
35
|
+
const tripDate = new Date(date);
|
|
36
|
+
if (tripDate > expDate || tripDate < effDate) return result; // Date before rate's effective date, or after rate's expiration date, move to next
|
|
37
|
+
|
|
38
|
+
const obj = {
|
|
39
|
+
city: city,
|
|
40
|
+
country: country,
|
|
41
|
+
label: city,
|
|
42
|
+
};
|
|
43
|
+
result.push(obj);
|
|
44
|
+
return result;
|
|
45
|
+
}, []);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export const getCitiesIntl = async (
|
|
49
|
+
date: DateRaw,
|
|
50
|
+
country: string,
|
|
51
|
+
): Promise<Location[]> => {
|
|
52
|
+
try {
|
|
53
|
+
return await fetchXmlDOD(getValidAPIYear(date))
|
|
54
|
+
.then(data => getCities(data, country))
|
|
55
|
+
.then(data => createLocations(data, date, country))
|
|
56
|
+
.then(data => sortLocations(data, 'city'))
|
|
57
|
+
.then(data => keepUniqueLocations(data, 'city'));
|
|
58
|
+
} catch (error) {
|
|
59
|
+
throw new Error(
|
|
60
|
+
`Failed to get int'l cities for ${date} - ${country} - ${error}`,
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
// Types
|
|
2
|
+
import type { Location } from '../../types/locations';
|
|
3
|
+
import type { DateRaw } from '../../types/dates';
|
|
4
|
+
import type { RateLodging } from '../../types/expenses';
|
|
5
|
+
|
|
6
|
+
// Utils
|
|
7
|
+
import { fetchJsonGSA } from '../fetch';
|
|
8
|
+
import { getValidAPIYear } from '../dates';
|
|
9
|
+
import { sortLocations } from './sortLocations';
|
|
10
|
+
import { keepUniqueLocations } from './keepUniqueLocations';
|
|
11
|
+
|
|
12
|
+
const createLocations = (data: RateLodging[]): Location[] => {
|
|
13
|
+
return data
|
|
14
|
+
.filter(rate => rate.State !== null && rate.State !== '')
|
|
15
|
+
.map(rate => {
|
|
16
|
+
return {
|
|
17
|
+
country: rate.State,
|
|
18
|
+
label: LIST_US_STATES.find(
|
|
19
|
+
state => state.country === rate.State,
|
|
20
|
+
)?.label,
|
|
21
|
+
};
|
|
22
|
+
});
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const getCountriesDomestic = async (
|
|
26
|
+
date: DateRaw,
|
|
27
|
+
): Promise<Location[]> => {
|
|
28
|
+
try {
|
|
29
|
+
return await fetchJsonGSA<RateLodging>(getValidAPIYear(date), 'lodging')
|
|
30
|
+
.then(data => createLocations(data))
|
|
31
|
+
.then(data => sortLocations(data, 'country'))
|
|
32
|
+
.then(data => keepUniqueLocations(data, 'country'));
|
|
33
|
+
} catch (error) {
|
|
34
|
+
throw new Error(
|
|
35
|
+
`Failed to get domestic countries for ${date} - ${error}`,
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const LIST_US_STATES: Location[] = [
|
|
41
|
+
{
|
|
42
|
+
country: 'AL',
|
|
43
|
+
label: 'Alabama (AL)',
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
country: 'AZ',
|
|
47
|
+
label: 'Arizona (AZ)',
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
country: 'AR',
|
|
51
|
+
label: 'Arkansas (AR)',
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
country: 'CA',
|
|
55
|
+
label: 'California (CA)',
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
country: 'CO',
|
|
59
|
+
label: 'Colorado (CO)',
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
country: 'CT',
|
|
63
|
+
label: 'Connecticut (CT)',
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
country: 'DE',
|
|
67
|
+
label: 'Delaware (DE)',
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
country: 'DC',
|
|
71
|
+
label: 'District Of Columbia (DC)',
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
country: 'FL',
|
|
75
|
+
label: 'Florida (FL)',
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
country: 'GA',
|
|
79
|
+
label: 'Georgia (GA)',
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
country: 'ID',
|
|
83
|
+
label: 'Idaho (ID)',
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
country: 'IL',
|
|
87
|
+
label: 'Illinois (IL)',
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
country: 'IN',
|
|
91
|
+
label: 'Indiana (IN)',
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
country: 'IA',
|
|
95
|
+
label: 'Iowa (IA)',
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
country: 'KS',
|
|
99
|
+
label: 'Kansas (KS)',
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
country: 'KY',
|
|
103
|
+
label: 'Kentucky (KY)',
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
country: 'LA',
|
|
107
|
+
label: 'Louisiana (LA)',
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
country: 'ME',
|
|
111
|
+
label: 'Maine (ME)',
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
country: 'MD',
|
|
115
|
+
label: 'Maryland (MD)',
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
country: 'MA',
|
|
119
|
+
label: 'Massachusetts (MA)',
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
country: 'MI',
|
|
123
|
+
label: 'Michigan (MI)',
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
country: 'MN',
|
|
127
|
+
label: 'Minnesota (MN)',
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
country: 'MS',
|
|
131
|
+
label: 'Mississippi (MS)',
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
country: 'MO',
|
|
135
|
+
label: 'Missouri (MO)',
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
country: 'MT',
|
|
139
|
+
label: 'Montana (MT)',
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
country: 'NE',
|
|
143
|
+
label: 'Nebraska (NE)',
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
country: 'NV',
|
|
147
|
+
label: 'Nevada (NV)',
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
country: 'NH',
|
|
151
|
+
label: 'New Hampshire (NH)',
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
country: 'NJ',
|
|
155
|
+
label: 'New Jersey (NJ)',
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
country: 'NM',
|
|
159
|
+
label: 'New Mexico (NM)',
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
country: 'NY',
|
|
163
|
+
label: 'New York (NY)',
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
country: 'NC',
|
|
167
|
+
label: 'North Carolina (NC)',
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
country: 'ND',
|
|
171
|
+
label: 'North Dakota (ND)',
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
country: 'OH',
|
|
175
|
+
label: 'Ohio (OH)',
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
country: 'OK',
|
|
179
|
+
label: 'Oklahoma (OK)',
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
country: 'OR',
|
|
183
|
+
label: 'Oregon (OR)',
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
country: 'PA',
|
|
187
|
+
label: 'Pennsylvania (PA)',
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
country: 'RI',
|
|
191
|
+
label: 'Rhode Island (RI)',
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
country: 'SC',
|
|
195
|
+
label: 'South Carolina (SC)',
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
country: 'SD',
|
|
199
|
+
label: 'South Dakota (SD)',
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
country: 'TN',
|
|
203
|
+
label: 'Tennessee (TN)',
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
country: 'TX',
|
|
207
|
+
label: 'Texas (TX)',
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
country: 'UT',
|
|
211
|
+
label: 'Utah (UT)',
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
country: 'VT',
|
|
215
|
+
label: 'Vermont (VT)',
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
country: 'VA',
|
|
219
|
+
label: 'Virginia (VA)',
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
country: 'WA',
|
|
223
|
+
label: 'Washington (WA)',
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
country: 'WV',
|
|
227
|
+
label: 'West Virginia (WV)',
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
country: 'WI',
|
|
231
|
+
label: 'Wisconsin (WI)',
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
country: 'WY',
|
|
235
|
+
label: 'Wyoming (WY)',
|
|
236
|
+
},
|
|
237
|
+
] as const;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// Types
|
|
2
|
+
import type { Location } from '../../types/locations';
|
|
3
|
+
import type { DateRaw } from '../../types/dates';
|
|
4
|
+
|
|
5
|
+
// Utils
|
|
6
|
+
import { fetchXmlDOD, parseXml } from '../fetch';
|
|
7
|
+
import { getValidAPIYear } from '../dates';
|
|
8
|
+
import { sortLocations } from './sortLocations';
|
|
9
|
+
import { keepUniqueLocations } from './keepUniqueLocations';
|
|
10
|
+
|
|
11
|
+
const createLocations = (data: Element[]): Location[] => {
|
|
12
|
+
return data.reduce((result: Location[], record) => {
|
|
13
|
+
const country = record.querySelector('country_name')?.textContent;
|
|
14
|
+
if (!country) return result;
|
|
15
|
+
const obj = {
|
|
16
|
+
country: country,
|
|
17
|
+
label: country,
|
|
18
|
+
};
|
|
19
|
+
result.push(obj);
|
|
20
|
+
return result;
|
|
21
|
+
}, []);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export const getCountriesIntl = async (date: DateRaw): Promise<Location[]> => {
|
|
25
|
+
try {
|
|
26
|
+
return await fetchXmlDOD(getValidAPIYear(date))
|
|
27
|
+
.then(data => parseXml(data, `//data/*`) as Element[])
|
|
28
|
+
.then(data => createLocations(data))
|
|
29
|
+
.then(data => sortLocations(data, 'country'))
|
|
30
|
+
.then(data => keepUniqueLocations(data, 'country'));
|
|
31
|
+
} catch (error) {
|
|
32
|
+
throw new Error(`Failed to get int'l countries for ${date} - ${error}`);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { getCountriesDomestic } from './getCountriesDomestic';
|
|
2
|
+
export { getCountriesIntl } from './getCountriesIntl';
|
|
3
|
+
export { getCitiesDomestic } from './getCitiesDomestic';
|
|
4
|
+
export { getCitiesIntl } from './getCitiesIntl';
|
|
5
|
+
export { returnValidStateLocation } from './returnValidStateLocation';
|
|
6
|
+
export { locationKeys } from './locationKeys';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Types
|
|
2
|
+
import type { Location } from '../../types/locations';
|
|
3
|
+
|
|
4
|
+
export const keepUniqueLocations = (
|
|
5
|
+
data: Location[],
|
|
6
|
+
key: 'country' | 'city',
|
|
7
|
+
): Location[] => {
|
|
8
|
+
return data.filter((record, i, arr) => {
|
|
9
|
+
if (i === 0) return true;
|
|
10
|
+
return record[key] !== arr[i - 1][key];
|
|
11
|
+
});
|
|
12
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Types
|
|
2
|
+
import type {
|
|
3
|
+
StateLocationItem,
|
|
4
|
+
StateLocationItemValid,
|
|
5
|
+
} from '../../types/locations';
|
|
6
|
+
|
|
7
|
+
export const returnValidStateLocation = (
|
|
8
|
+
location: StateLocationItem,
|
|
9
|
+
): StateLocationItemValid | null => {
|
|
10
|
+
const { start, end, country, city } = location;
|
|
11
|
+
if (!(start && end && country && city)) return null;
|
|
12
|
+
return { start, end, country, city };
|
|
13
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// Types
|
|
2
|
+
import type { Location } from '../../types/locations';
|
|
3
|
+
|
|
4
|
+
export const sortLocations = (
|
|
5
|
+
data: Location[],
|
|
6
|
+
key: 'country' | 'city',
|
|
7
|
+
domesticCities: 'domesticCities' | null = null,
|
|
8
|
+
): Location[] => {
|
|
9
|
+
if (!domesticCities)
|
|
10
|
+
return data.sort((a, b) => (a[key] || '').localeCompare(b[key] || ''));
|
|
11
|
+
|
|
12
|
+
return data
|
|
13
|
+
.filter(rate => rate[key] === 'Standard Rate')
|
|
14
|
+
.concat(
|
|
15
|
+
data
|
|
16
|
+
.filter(rate => rate[key] !== 'Standard Rate')
|
|
17
|
+
.sort((a, b) => (a[key] || '').localeCompare(b[key] || '')),
|
|
18
|
+
);
|
|
19
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
// Utils
|
|
3
|
+
import { DEBOUNCE_TIME } from '../config';
|
|
4
|
+
|
|
5
|
+
type TimeoutId = ReturnType<typeof setTimeout> | null;
|
|
6
|
+
type DebouncedFunction = (...args: any[]) => void;
|
|
7
|
+
|
|
8
|
+
export const debounce = (
|
|
9
|
+
callback: DebouncedFunction,
|
|
10
|
+
delay: number = DEBOUNCE_TIME,
|
|
11
|
+
): DebouncedFunction => {
|
|
12
|
+
let timeoutId: TimeoutId = null;
|
|
13
|
+
return (...args: any[]): void => {
|
|
14
|
+
if (timeoutId) {
|
|
15
|
+
clearTimeout(timeoutId);
|
|
16
|
+
}
|
|
17
|
+
timeoutId = setTimeout(() => {
|
|
18
|
+
callback(...args);
|
|
19
|
+
timeoutId = null; // Reset the timeout
|
|
20
|
+
}, delay);
|
|
21
|
+
};
|
|
22
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const tolerance = 5;
|
|
2
|
+
|
|
3
|
+
export const handlePointerUp = (
|
|
4
|
+
event: PointerEvent,
|
|
5
|
+
clickFunction: (e: Event) => void,
|
|
6
|
+
pointerStartX: number,
|
|
7
|
+
pointerStartY: number,
|
|
8
|
+
) => {
|
|
9
|
+
const pointerEndX = event.clientX;
|
|
10
|
+
const pointerEndY = event.clientY;
|
|
11
|
+
|
|
12
|
+
const deltaX = Math.abs(pointerEndX - pointerStartX);
|
|
13
|
+
const deltaY = Math.abs(pointerEndY - pointerStartY);
|
|
14
|
+
|
|
15
|
+
if (deltaX <= tolerance && deltaY <= tolerance && event.button === 0) {
|
|
16
|
+
// This was a tap or a click, not a drag (no significant movement)
|
|
17
|
+
clickFunction(event);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Reset pointer coordinates
|
|
21
|
+
return { pointerStartX: 0, pointerStartY: 0 };
|
|
22
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { inPrimitiveType } from './inPrimitiveType';
|
|
2
|
+
export { handlePointerDown } from './handlePointerDown';
|
|
3
|
+
export { handlePointerUp } from './handlePointerUp';
|
|
4
|
+
export { USD } from './USD';
|
|
5
|
+
export { debounce } from './debounce';
|
|
6
|
+
export { wait } from './wait';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// Workaround to fully apply Tailwind styles to Shadow DOM
|
|
2
|
+
// https://github.com/tailwindlabs/tailwindcss/issues/15005#issuecomment-2737489813
|
|
3
|
+
|
|
4
|
+
import styles from '../../../css/_styles.css?inline';
|
|
5
|
+
|
|
6
|
+
export const applyStyles = (shadowRoot: ShadowRoot) => {
|
|
7
|
+
const shadowSheet = new CSSStyleSheet();
|
|
8
|
+
shadowSheet.replaceSync(styles.replace(/:root/gu, ':host'));
|
|
9
|
+
const properties = [];
|
|
10
|
+
for (const rule of shadowSheet.cssRules) {
|
|
11
|
+
if (rule instanceof CSSPropertyRule) {
|
|
12
|
+
if (rule.initialValue) {
|
|
13
|
+
properties.push(`${rule.name}: ${rule.initialValue}`);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
shadowSheet.insertRule(`:host { ${properties.join('; ')} }`);
|
|
18
|
+
shadowRoot.adoptedStyleSheets = [shadowSheet];
|
|
19
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { BTN_ANIMATE_MS } from '../config';
|
|
2
|
+
|
|
3
|
+
export const highlightSuccess = (input: HTMLElement | SVGElement) => {
|
|
4
|
+
input.classList.toggle('success');
|
|
5
|
+
setTimeout(() => {
|
|
6
|
+
input.classList.toggle('success');
|
|
7
|
+
}, BTN_ANIMATE_MS);
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const highlightError = (input: HTMLElement | SVGElement) => {
|
|
11
|
+
input.classList.toggle('error');
|
|
12
|
+
setTimeout(() => {
|
|
13
|
+
input.classList.toggle('error');
|
|
14
|
+
}, BTN_ANIMATE_MS);
|
|
15
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export const removeStyles = (HTML: string) => {
|
|
2
|
+
// Remove all instances of `class="..."`
|
|
3
|
+
// Remove all instances of `styled="true"`
|
|
4
|
+
// Remove all SVG elements
|
|
5
|
+
return HTML.replaceAll(/class="[^"]*"/gi, '')
|
|
6
|
+
.replaceAll('styled="true"', '')
|
|
7
|
+
.replaceAll(
|
|
8
|
+
/<svg[^>]*data-pdc-unstyled="([^"]*)"[^>]*>[\s\S]*?<\/svg>/gi,
|
|
9
|
+
(_, attributeValue) => {
|
|
10
|
+
return attributeValue;
|
|
11
|
+
},
|
|
12
|
+
)
|
|
13
|
+
.replaceAll(/<svg[\s\S]*?<\/svg>/gi, '');
|
|
14
|
+
};
|