@xy-planning-network/trees 0.11.4 → 0.11.5
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/dist/trees.es.js +5431 -4358
- package/dist/trees.umd.js +18 -12
- package/package.json +4 -1
- package/src/lib-components/forms/NumberInput.vue +172 -0
- package/types/composables/forms.d.ts +9 -0
- package/types/composables/table.d.ts +5 -0
- package/types/composables/useTable.d.ts +2 -0
- package/types/entry.d.ts +2 -2
- package/types/lib-components/forms/NumberInput.vue.d.ts +24 -0
- package/types/lib-components/index.d.ts +3 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xy-planning-network/trees",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.5",
|
|
4
4
|
"description": "",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": "github:xy-planning-network/trees",
|
|
@@ -60,6 +60,9 @@
|
|
|
60
60
|
"@floating-ui/vue": "^1.0.1",
|
|
61
61
|
"@headlessui/vue": "^1.4.2",
|
|
62
62
|
"@heroicons/vue": "^1.0.5",
|
|
63
|
+
"@maskito/core": "^3.5.0",
|
|
64
|
+
"@maskito/kit": "^3.5.0",
|
|
65
|
+
"@maskito/vue": "^3.5.0",
|
|
63
66
|
"axios": "^1.5.0",
|
|
64
67
|
"flatpickr": "^4.6.9"
|
|
65
68
|
},
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import InputLabel from "./InputLabel.vue"
|
|
3
|
+
import InputHelp from "./InputHelp.vue"
|
|
4
|
+
import InputError from "./InputError.vue"
|
|
5
|
+
import {
|
|
6
|
+
useInputField,
|
|
7
|
+
defaultInputProps,
|
|
8
|
+
defaultModelOpts,
|
|
9
|
+
} from "@/composables/forms"
|
|
10
|
+
import type { NumericInput } from "@/composables/forms"
|
|
11
|
+
import { computed, useAttrs, useTemplateRef } from "vue"
|
|
12
|
+
import { maskito as vMaskito } from "@maskito/vue"
|
|
13
|
+
import { maskitoTransform } from "@maskito/core"
|
|
14
|
+
import {
|
|
15
|
+
MaskitoNumberParams,
|
|
16
|
+
maskitoNumberOptionsGenerator,
|
|
17
|
+
maskitoParseNumber,
|
|
18
|
+
} from "@maskito/kit"
|
|
19
|
+
|
|
20
|
+
defineOptions({
|
|
21
|
+
inheritAttrs: false,
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
const props = withDefaults(defineProps<NumericInput>(), {
|
|
25
|
+
...defaultInputProps,
|
|
26
|
+
max: Number.MAX_SAFE_INTEGER,
|
|
27
|
+
min: Number.MIN_SAFE_INTEGER,
|
|
28
|
+
precision: 0,
|
|
29
|
+
type: "number",
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
const opts = computed(() => {
|
|
33
|
+
const params: MaskitoNumberParams = {
|
|
34
|
+
decimalSeparator: ".",
|
|
35
|
+
thousandSeparator: ",",
|
|
36
|
+
min: props.min,
|
|
37
|
+
max: props.max,
|
|
38
|
+
precision: props.precision,
|
|
39
|
+
minusSign: "-",
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
switch (props.type) {
|
|
43
|
+
case "money":
|
|
44
|
+
params.decimalZeroPadding = true
|
|
45
|
+
params.precision = 2
|
|
46
|
+
params.prefix = "$"
|
|
47
|
+
break
|
|
48
|
+
|
|
49
|
+
default:
|
|
50
|
+
break
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return maskitoNumberOptionsGenerator(params)
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
const modelState = defineModel<NumericInput["modelValue"]>(defaultModelOpts)
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* masked is a writable computed variable that ensures the modelState
|
|
60
|
+
* is only set to a valid numeric value as the masked state is a string which
|
|
61
|
+
* may contain prefix and postfix characters as well as leading or trailing "-", and "." characters.
|
|
62
|
+
*
|
|
63
|
+
* It additionally handles the computation of converting a "money" input to an amount in U.S. cents.
|
|
64
|
+
*/
|
|
65
|
+
const masked = computed({
|
|
66
|
+
get: () => {
|
|
67
|
+
if (modelState.value === null || modelState.value === undefined) {
|
|
68
|
+
return ""
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
let num = modelState.value
|
|
72
|
+
if (props.type === "money") {
|
|
73
|
+
num = num / 100
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return maskitoTransform(num.toString(), opts.value)
|
|
77
|
+
},
|
|
78
|
+
set: (v) => {
|
|
79
|
+
if (v === "") {
|
|
80
|
+
modelState.value = null
|
|
81
|
+
return
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
let number = maskitoParseNumber(v)
|
|
85
|
+
if (isNaN(number)) {
|
|
86
|
+
modelState.value = null
|
|
87
|
+
return
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// NOTE(spk): There's no Integer type in JavaScript all numbers are stored as floating point values
|
|
91
|
+
// to avoid approximations from decimal values avoid calling parseFloat and use parseInt on only the dollar
|
|
92
|
+
// portion when converting from string to number.
|
|
93
|
+
if (props.type === "money") {
|
|
94
|
+
let parts = number.toString().split(".")
|
|
95
|
+
let dollars = parseInt(parts[0], 10)
|
|
96
|
+
let cents = parts.length > 1 ? parseInt(parts[1].padEnd(2, "0"), 10) : 0
|
|
97
|
+
number = dollars * 100 + cents
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
modelState.value = number
|
|
101
|
+
},
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
const inputRef = useTemplateRef("input")
|
|
105
|
+
|
|
106
|
+
const {
|
|
107
|
+
errorState,
|
|
108
|
+
inputID,
|
|
109
|
+
isRequired,
|
|
110
|
+
nameAttr,
|
|
111
|
+
onInvalid,
|
|
112
|
+
inputValidation,
|
|
113
|
+
} = useInputField(props)
|
|
114
|
+
|
|
115
|
+
// NOTE(spk): reserve the name attribute for the hidden number input.
|
|
116
|
+
const inputAttrs = computed(() => {
|
|
117
|
+
const attributes = useAttrs()
|
|
118
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
119
|
+
const { name, ...attrs } = attributes
|
|
120
|
+
return attrs
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
defineExpose({ input: inputRef })
|
|
124
|
+
</script>
|
|
125
|
+
|
|
126
|
+
<template>
|
|
127
|
+
<div>
|
|
128
|
+
<InputLabel
|
|
129
|
+
:id="`${inputID}-label`"
|
|
130
|
+
class="mb-2"
|
|
131
|
+
:for="inputID"
|
|
132
|
+
:label="label"
|
|
133
|
+
:required="isRequired"
|
|
134
|
+
/>
|
|
135
|
+
<input
|
|
136
|
+
:id="inputID"
|
|
137
|
+
ref="input"
|
|
138
|
+
v-model="masked"
|
|
139
|
+
v-maskito="opts"
|
|
140
|
+
:aria-labelledby="label ? `${inputID}-label` : undefined"
|
|
141
|
+
:aria-describedby="help ? `${inputID}-help` : undefined"
|
|
142
|
+
:aria-errormessage="errorState ? `${inputID}-error` : undefined"
|
|
143
|
+
:class="[
|
|
144
|
+
'block w-full rounded-md border-0 py-2 shadow-sm ring-1 ring-inset focus:ring-2 sm:text-sm sm:leading-6',
|
|
145
|
+
'disabled:cursor-not-allowed disabled:bg-gray-50 disabled:text-gray-700 disabled:ring-gray-200',
|
|
146
|
+
errorState
|
|
147
|
+
? 'text-red-900 ring-red-700 placeholder:text-red-300 focus:ring-red-700'
|
|
148
|
+
: 'text-gray-900 ring-gray-300 placeholder:text-gray-400 focus:ring-xy-blue-500',
|
|
149
|
+
]"
|
|
150
|
+
:placeholder="placeholder"
|
|
151
|
+
type="text"
|
|
152
|
+
inputmode="decimal"
|
|
153
|
+
v-bind="{ ...inputAttrs }"
|
|
154
|
+
@input="inputValidation"
|
|
155
|
+
/>
|
|
156
|
+
|
|
157
|
+
<InputHelp :id="`${inputID}-help`" class="mt-1" :text="help" />
|
|
158
|
+
<InputError :id="`${inputID}-error`" class="mt-0.5" :text="errorState" />
|
|
159
|
+
<input
|
|
160
|
+
:value="modelState"
|
|
161
|
+
class="sr-only"
|
|
162
|
+
aria-hidden="true"
|
|
163
|
+
:name="nameAttr"
|
|
164
|
+
:min="min"
|
|
165
|
+
:max="max"
|
|
166
|
+
:required="isRequired"
|
|
167
|
+
tabindex="-1"
|
|
168
|
+
type="number"
|
|
169
|
+
@invalid="onInvalid"
|
|
170
|
+
/>
|
|
171
|
+
</div>
|
|
172
|
+
</template>
|
|
@@ -38,6 +38,13 @@ export interface TextLikeInput extends Input {
|
|
|
38
38
|
modelValue?: string | number | null;
|
|
39
39
|
type: TextInputType;
|
|
40
40
|
}
|
|
41
|
+
export interface NumericInput extends Input {
|
|
42
|
+
max?: number;
|
|
43
|
+
min?: number;
|
|
44
|
+
modelValue?: number | null;
|
|
45
|
+
precision?: number;
|
|
46
|
+
type?: NumericInputType;
|
|
47
|
+
}
|
|
41
48
|
export interface TextareaInput extends Input {
|
|
42
49
|
modelValue?: string | number | null;
|
|
43
50
|
}
|
|
@@ -77,6 +84,8 @@ export declare const defaultModelOpts: {
|
|
|
77
84
|
};
|
|
78
85
|
export declare const textInputTypes: readonly ["date", "email", "month", "number", "password", "search", "tel", "text", "time", "url", "week"];
|
|
79
86
|
export type TextInputType = (typeof textInputTypes)[number];
|
|
87
|
+
export declare const numericInputTypes: readonly ["money", "number"];
|
|
88
|
+
export type NumericInputType = (typeof numericInputTypes)[number];
|
|
80
89
|
/**
|
|
81
90
|
* useInputField provides a number of computed values, refs, and methods to support
|
|
82
91
|
* wiring up reactive inputs.
|
|
@@ -77,6 +77,11 @@ export interface TableColumn<T = TableRowData> {
|
|
|
77
77
|
* This may include returning a custom component using the vue h method
|
|
78
78
|
*/
|
|
79
79
|
render: keyof T | ((rowData: T, rowIndex: number) => string | number | boolean | VNodeChild);
|
|
80
|
+
/**
|
|
81
|
+
* A show condition for determining whether the column should be shown in the table.
|
|
82
|
+
* Use this condition for supporting dynamic column visibility.
|
|
83
|
+
*/
|
|
84
|
+
show?: boolean;
|
|
80
85
|
/**
|
|
81
86
|
* A sorting identifier
|
|
82
87
|
* Only used on DynamicTable
|
|
@@ -6,6 +6,7 @@ export declare const useTable: (rowData: TableRowsData | Ref<TableRowsData>, col
|
|
|
6
6
|
classNames?: string | ((rowData: import("./table").TableRowData, rowIndex: number) => string) | undefined;
|
|
7
7
|
title: string;
|
|
8
8
|
render: string | ((rowData: import("./table").TableRowData, rowIndex: number) => import("vue").VNodeChild);
|
|
9
|
+
show?: boolean | undefined;
|
|
9
10
|
sort?: string | undefined;
|
|
10
11
|
}[]>;
|
|
11
12
|
hasActions: import("vue").ComputedRef<boolean>;
|
|
@@ -26,6 +27,7 @@ export declare const useTable: (rowData: TableRowsData | Ref<TableRowsData>, col
|
|
|
26
27
|
alignment: string;
|
|
27
28
|
title: string;
|
|
28
29
|
render: string | ((rowData: import("./table").TableRowData, rowIndex: number) => import("vue").VNodeChild);
|
|
30
|
+
show?: boolean | undefined;
|
|
29
31
|
sort?: string | undefined;
|
|
30
32
|
}[];
|
|
31
33
|
}[]>;
|
package/types/entry.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Plugin } from "vue";
|
|
2
2
|
import BaseAPI, { isHttpCancel, isHttpError, setBaseAPIDefaults } from "./api/base";
|
|
3
3
|
import type { HttpPromise, HttpError, QueryParams, ReqOptions, ReqPayload, TrailsPromise, TrailsPromisePaged, TrailsResp, TrailsRespPaged } from "./api/client";
|
|
4
|
-
import { emailPattern, looseToNumber, phonePattern, urlPattern, textInputTypes, useInputField } from "./composables/forms";
|
|
4
|
+
import { emailPattern, looseToNumber, numericInputTypes, phonePattern, urlPattern, textInputTypes, useInputField } from "./composables/forms";
|
|
5
5
|
import { debounce as debounceFn, debounceLeading } from "./helpers/Debounce";
|
|
6
6
|
import { throttle as throttleFn } from "./helpers/Throttle";
|
|
7
7
|
declare const install: Exclude<Plugin["install"], undefined>;
|
|
@@ -11,5 +11,5 @@ export * from "./lib-components/index";
|
|
|
11
11
|
export { BaseAPI, isHttpCancel, isHttpError, setBaseAPIDefaults };
|
|
12
12
|
export type { HttpPromise, HttpError, QueryParams, ReqOptions, ReqPayload, TrailsPromise, TrailsPromisePaged, TrailsResp, TrailsRespPaged, };
|
|
13
13
|
export type * from "./composables/forms";
|
|
14
|
-
export { emailPattern, looseToNumber, phonePattern, urlPattern, textInputTypes, useInputField, };
|
|
14
|
+
export { emailPattern, looseToNumber, phonePattern, urlPattern, numericInputTypes, textInputTypes, useInputField, };
|
|
15
15
|
export { debounceFn, debounceLeading, throttleFn };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { NumericInput } from "../../composables/forms";
|
|
2
|
+
type __VLS_Props = NumericInput;
|
|
3
|
+
type __VLS_PublicProps = {
|
|
4
|
+
modelValue?: NumericInput["modelValue"];
|
|
5
|
+
} & __VLS_Props;
|
|
6
|
+
declare const _default: import("vue").DefineComponent<__VLS_PublicProps, {
|
|
7
|
+
input: Readonly<import("vue").ShallowRef<HTMLInputElement | null>>;
|
|
8
|
+
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
9
|
+
"update:modelValue": (value: number | null | undefined) => any;
|
|
10
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
11
|
+
"onUpdate:modelValue"?: ((value: number | null | undefined) => any) | undefined;
|
|
12
|
+
}>, {
|
|
13
|
+
label: string;
|
|
14
|
+
type: "number" | "money";
|
|
15
|
+
modelValue: number | null;
|
|
16
|
+
max: number;
|
|
17
|
+
min: number;
|
|
18
|
+
help: string;
|
|
19
|
+
placeholder: string;
|
|
20
|
+
precision: number;
|
|
21
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {
|
|
22
|
+
input: HTMLInputElement;
|
|
23
|
+
}, any>;
|
|
24
|
+
export default _default;
|
|
@@ -33,13 +33,14 @@ import { default as InputHelp } from "./forms/InputHelp.vue";
|
|
|
33
33
|
import { default as InputLabel } from "./forms/InputLabel.vue";
|
|
34
34
|
import { default as FieldsetLegend } from "./forms/FieldsetLegend.vue";
|
|
35
35
|
import { default as MultiCheckboxes } from "./forms/MultiCheckboxes.vue";
|
|
36
|
+
import { default as NumberInput } from "./forms/NumberInput.vue";
|
|
36
37
|
import { default as Radio } from "./forms/Radio.vue";
|
|
37
38
|
import { default as RadioCards } from "./forms/RadioCards.vue";
|
|
38
39
|
import { default as Select } from "./forms/Select.vue";
|
|
39
40
|
import { default as TextArea } from "./forms/TextArea.vue";
|
|
40
41
|
import { default as YesOrNoRadio } from "./forms/YesOrNoRadio.vue";
|
|
41
42
|
export { ActionsDropdown, Cards, ContentModal, DateFilter, DetailList, DownloadCell, Flash, InlineAlert, Modal, SidebarLayout, Slideover, StackedLayout, Popover, PopoverContent, PopoverPosition, // Type export
|
|
42
|
-
Paginator, Spinner, DataTable, Steps, DynamicTable, Tabs, Toggle, Tooltip, BaseInput, Checkbox, DateRangePicker, DateTime, InputError, InputHelp, InputLabel, FieldsetLegend, MultiCheckboxes, Radio, RadioCards, Select, TextArea, YesOrNoRadio, XYSpinner, ProgressCircles, ProgressCirclesLabeled, };
|
|
43
|
+
Paginator, Spinner, DataTable, Steps, DynamicTable, Tabs, Toggle, Tooltip, BaseInput, Checkbox, DateRangePicker, DateTime, InputError, InputHelp, InputLabel, FieldsetLegend, MultiCheckboxes, NumberInput, Radio, RadioCards, Select, TextArea, YesOrNoRadio, XYSpinner, ProgressCircles, ProgressCirclesLabeled, };
|
|
43
44
|
/**
|
|
44
45
|
* declare global component types for App.use(Trees)
|
|
45
46
|
*/
|
|
@@ -74,6 +75,7 @@ export interface TreesComponents {
|
|
|
74
75
|
InputLabel: typeof InputLabel;
|
|
75
76
|
FieldsetLegend: typeof FieldsetLegend;
|
|
76
77
|
MultiCheckboxes: typeof MultiCheckboxes;
|
|
78
|
+
NumberInput: typeof NumberInput;
|
|
77
79
|
Radio: typeof Radio;
|
|
78
80
|
RadioCards: typeof RadioCards;
|
|
79
81
|
Select: typeof Select;
|