@xy-planning-network/trees 0.11.3 → 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xy-planning-network/trees",
3
- "version": "0.11.3",
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
  },
@@ -16,8 +16,12 @@ defineOptions({
16
16
  inheritAttrs: false,
17
17
  })
18
18
 
19
+ // maxDate/startDate should be used or maxRange.
20
+ // The props combination of maxDate/startDate and maxRange
21
+ // will have unexpected results.
19
22
  const props = withDefaults(defineProps<DateRangeInput>(), {
20
23
  ...defaultInputProps,
24
+ maxDate: () => new Date(),
21
25
  maxRange: 0,
22
26
  placeholder: "mm-dd-yyyy range",
23
27
  startDate: 0,
@@ -39,7 +43,7 @@ onMounted(() => {
39
43
  allowInput: true,
40
44
  dateFormat: "m-d-Y",
41
45
  mode: "range",
42
- maxDate: new Date(), // So far, we cannot have options past today for ranges
46
+ maxDate: props.maxDate,
43
47
  minDate: props.startDate,
44
48
  onClose: (selectedDates) => {
45
49
  if (selectedDates.length === 2) {
@@ -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>
@@ -5,6 +5,7 @@ export interface DateRange {
5
5
  export interface DateRangeProps {
6
6
  help?: string;
7
7
  label?: string;
8
+ maxDate?: Date | string | number;
8
9
  maxRange?: number;
9
- startDate?: number;
10
+ startDate?: Date | string | number;
10
11
  }
@@ -14,13 +14,22 @@ export interface InputOption {
14
14
  export interface BooleanInput extends Input {
15
15
  modelValue?: boolean | null;
16
16
  }
17
+ /**
18
+ * DateRangeInput allows a max range to be applied to the datepicker
19
+ * such that selecting the first date will then apply a max/min of +/- maxRange.
20
+ * This does not play nicely with maxDate and startDate.
21
+ *
22
+ * ex: when submitting a startDate of Jan 1 and maxRange of 10, the date picker will allow
23
+ * dates prior to Jan 1 given the handling of maxRange being adjusted on the fly.
24
+ */
17
25
  export interface DateRangeInput extends Input {
18
26
  modelValue?: {
19
27
  minDate: number;
20
28
  maxDate: number;
21
29
  };
30
+ maxDate?: Date | string | number;
22
31
  maxRange?: number;
23
- startDate?: number;
32
+ startDate?: Date | string | number;
24
33
  }
25
34
  export interface DateTimeInput extends Input {
26
35
  modelValue?: string | null;
@@ -29,6 +38,13 @@ export interface TextLikeInput extends Input {
29
38
  modelValue?: string | number | null;
30
39
  type: TextInputType;
31
40
  }
41
+ export interface NumericInput extends Input {
42
+ max?: number;
43
+ min?: number;
44
+ modelValue?: number | null;
45
+ precision?: number;
46
+ type?: NumericInputType;
47
+ }
32
48
  export interface TextareaInput extends Input {
33
49
  modelValue?: string | number | null;
34
50
  }
@@ -68,6 +84,8 @@ export declare const defaultModelOpts: {
68
84
  };
69
85
  export declare const textInputTypes: readonly ["date", "email", "month", "number", "password", "search", "tel", "text", "time", "url", "week"];
70
86
  export type TextInputType = (typeof textInputTypes)[number];
87
+ export declare const numericInputTypes: readonly ["money", "number"];
88
+ export type NumericInputType = (typeof numericInputTypes)[number];
71
89
  /**
72
90
  * useInputField provides a number of computed values, refs, and methods to support
73
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 };
@@ -20,8 +20,9 @@ declare const _default: import("vue").DefineComponent<__VLS_PublicProps, {}, {},
20
20
  minDate: number;
21
21
  maxDate: number;
22
22
  };
23
+ maxDate: string | number | Date;
23
24
  maxRange: number;
24
- startDate: number;
25
+ startDate: string | number | Date;
25
26
  help: string;
26
27
  placeholder: string;
27
28
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
@@ -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;