@xy-planning-network/trees 0.7.4 → 0.7.5-rc1

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.
Files changed (58) hide show
  1. package/dist/trees.es.js +3939 -3561
  2. package/dist/trees.umd.js +10 -10
  3. package/package.json +2 -2
  4. package/src/index.css +0 -16
  5. package/src/lib-components/forms/BaseInput.vue +90 -75
  6. package/src/lib-components/forms/Checkbox.vue +50 -34
  7. package/src/lib-components/forms/DateRangePicker.vue +56 -32
  8. package/src/lib-components/forms/FieldsetLegend.vue +28 -8
  9. package/src/lib-components/forms/InputHelp.vue +2 -4
  10. package/src/lib-components/forms/InputLabel.vue +27 -12
  11. package/src/lib-components/forms/MultiCheckboxes.vue +117 -74
  12. package/src/lib-components/forms/Radio.vue +79 -66
  13. package/src/lib-components/forms/RadioCards.vue +72 -70
  14. package/src/lib-components/forms/Select.vue +59 -56
  15. package/src/lib-components/forms/TextArea.vue +54 -47
  16. package/src/lib-components/forms/Toggle.vue +61 -18
  17. package/src/lib-components/forms/YesOrNoRadio.vue +75 -67
  18. package/src/lib-components/lists/DynamicTable.vue +43 -20
  19. package/types/composables/forms.d.ts +118 -0
  20. package/types/helpers/Debounce.d.ts +1 -1
  21. package/types/lib-components/forms/BaseInput.vue.d.ts +58 -34
  22. package/types/lib-components/forms/Checkbox.vue.d.ts +50 -29
  23. package/types/lib-components/forms/DateRangePicker.vue.d.ts +71 -39
  24. package/types/lib-components/forms/FieldsetLegend.vue.d.ts +30 -31
  25. package/types/lib-components/forms/InputHelp.vue.d.ts +19 -27
  26. package/types/lib-components/forms/InputLabel.vue.d.ts +28 -32
  27. package/types/lib-components/forms/MultiCheckboxes.vue.d.ts +77 -56
  28. package/types/lib-components/forms/Radio.vue.d.ts +64 -56
  29. package/types/lib-components/forms/RadioCards.vue.d.ts +68 -71
  30. package/types/lib-components/forms/Select.vue.d.ts +55 -44
  31. package/types/lib-components/forms/TextArea.vue.d.ts +50 -32
  32. package/types/lib-components/forms/Toggle.vue.d.ts +29 -24
  33. package/types/lib-components/forms/YesOrNoRadio.vue.d.ts +51 -38
  34. package/types/lib-components/indicators/XYSpinner.vue.d.ts +1 -1
  35. package/types/lib-components/layout/DateFilter.vue.d.ts +28 -20
  36. package/types/lib-components/layout/SidebarLayout.vue.d.ts +38 -32
  37. package/types/lib-components/layout/StackedLayout.vue.d.ts +45 -33
  38. package/types/lib-components/lists/Cards.vue.d.ts +14 -17
  39. package/types/lib-components/lists/DataTable.vue.d.ts +31 -31
  40. package/types/lib-components/lists/DetailList.vue.d.ts +38 -34
  41. package/types/lib-components/lists/DownloadCell.vue.d.ts +18 -15
  42. package/types/lib-components/lists/DynamicTable.vue.d.ts +32 -32
  43. package/types/lib-components/lists/StaticTable.vue.d.ts +21 -0
  44. package/types/lib-components/lists/StaticTableActionSlot.vue.d.ts +27 -0
  45. package/types/lib-components/lists/Table.vue.d.ts +39 -0
  46. package/types/lib-components/lists/TableActionButtons.vue.d.ts +11 -23
  47. package/types/lib-components/navigation/ActionsDropdown.vue.d.ts +11 -23
  48. package/types/lib-components/navigation/ActionsDropdownCallback.vue.d.ts +18 -0
  49. package/types/lib-components/navigation/ActionsDropdownEmit.vue.d.ts +22 -0
  50. package/types/lib-components/navigation/Paginator.vue.d.ts +12 -15
  51. package/types/lib-components/navigation/Steps.vue.d.ts +54 -39
  52. package/types/lib-components/navigation/Tabs.vue.d.ts +34 -34
  53. package/types/lib-components/overlays/ContentModal.vue.d.ts +22 -28
  54. package/types/lib-components/overlays/Modal.vue.d.ts +48 -43
  55. package/types/lib-components/overlays/Popover/Popover.vue.d.ts +23 -31
  56. package/types/lib-components/overlays/Popover/PopoverContent.vue.d.ts +1 -1
  57. package/types/lib-components/overlays/Slideover.vue.d.ts +30 -22
  58. package/types/lib-components/overlays/Tooltip.vue.d.ts +20 -28
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xy-planning-network/trees",
3
- "version": "0.7.4",
3
+ "version": "0.7.5-rc1",
4
4
  "description": "",
5
5
  "license": "MIT",
6
6
  "repository": "github:xy-planning-network/trees",
@@ -52,7 +52,7 @@
52
52
  "tsc-alias": "^1.8.5",
53
53
  "typescript": "^5.0.4",
54
54
  "vite": "^4.3.9",
55
- "vue-tsc": "^1.2.0"
55
+ "vue-tsc": "^1.8.18"
56
56
  },
57
57
  "dependencies": {
58
58
  "@floating-ui/vue": "^1.0.1",
package/src/index.css CHANGED
@@ -32,17 +32,6 @@
32
32
  h6 {
33
33
  @apply text-xs leading-4 font-semibold;
34
34
  }
35
-
36
- /* Forms: here for backward compatibility. Use <BaseInput> instead. */
37
- [type="text"],
38
- [type="email"],
39
- [type="password"],
40
- [type="number"],
41
- [type="search"],
42
- [type="tel"],
43
- textarea {
44
- @apply mt-1 shadow-sm focus:ring-xy-blue-500 focus:border-xy-blue block w-full sm:text-sm border-gray-600 rounded-md;
45
- }
46
35
  }
47
36
 
48
37
  @layer components {
@@ -131,9 +120,4 @@
131
120
  .max-h-screen-1\/2 {
132
121
  max-height: 50vh;
133
122
  }
134
-
135
- /* Forms */
136
- .xy-input-error {
137
- @apply focus:ring-red-700 focus:border-red-700 text-red-900 placeholder-red-700 placeholder-opacity-75 border-red-700;
138
- }
139
123
  }
@@ -1,87 +1,102 @@
1
1
  <script setup lang="ts">
2
- import Uniques from "@/helpers/Uniques"
3
2
  import InputLabel from "./InputLabel.vue"
4
3
  import InputHelp from "./InputHelp.vue"
5
- import { computed, useAttrs } from "vue"
4
+ import {
5
+ useInputField,
6
+ defaultInputProps,
7
+ emailPattern,
8
+ looseToNumber,
9
+ passwordPattern,
10
+ phonePattern,
11
+ } from "@/composables/forms"
12
+ import type { TextLikeInput } from "@/composables/forms"
13
+ import { computed, ref } from "vue"
6
14
 
7
- const attrs = useAttrs()
8
- const props = withDefaults(
9
- defineProps<{
10
- type: string
11
- help?: string
12
- label?: string
13
- modelValue?: string | number
14
- }>(),
15
- {
16
- help: "",
17
- label: "",
18
- modelValue: "",
15
+ defineOptions({
16
+ inheritAttrs: false,
17
+ })
18
+
19
+ const props = withDefaults(defineProps<TextLikeInput>(), defaultInputProps)
20
+
21
+ defineEmits(["update:modelValue", "update:error"])
22
+ const targetInput = ref<HTMLInputElement | null>(null)
23
+ const {
24
+ errorState,
25
+ modelState,
26
+ inputID,
27
+ isRequired,
28
+ onInvalid,
29
+ inputValidation,
30
+ } = useInputField({ props, targetInput })
31
+
32
+ const typeAttributes = computed(() => {
33
+ switch (props.type) {
34
+ case "number":
35
+ return {
36
+ max: Number.MAX_SAFE_INTEGER,
37
+ min: Number.MIN_SAFE_INTEGER,
38
+ }
39
+ case "email":
40
+ return {
41
+ pattern: emailPattern,
42
+ }
43
+ case "password":
44
+ return {
45
+ pattern: passwordPattern,
46
+ }
47
+ case "tel":
48
+ return {
49
+ pattern: phonePattern,
50
+ }
51
+ default:
52
+ return {}
19
53
  }
20
- )
54
+ })
21
55
 
22
- const emit = defineEmits(["update:modelValue"])
56
+ const onInput = (e: Event) => {
57
+ let val = (e.target as HTMLInputElement).value
23
58
 
24
- const uuid = (attrs.id as string) || Uniques.CreateIdAttribute()
59
+ if (props.type === "number") {
60
+ val = looseToNumber(val)
61
+ }
25
62
 
26
- /**
27
- * common text based inputs
28
- */
29
- const textInputTypes = [
30
- "date",
31
- "datetime-local",
32
- "email",
33
- "month",
34
- "number",
35
- "password",
36
- "search",
37
- "tel",
38
- "text",
39
- "time",
40
- "url",
41
- "week",
42
- ]
63
+ modelState.value = val
43
64
 
44
- /**
45
- * determine if this input is a common text based input
46
- */
47
- const isTextType = computed((): boolean => {
48
- return typeof props.type === "string" && textInputTypes.includes(props.type)
49
- })
65
+ inputValidation(e)
66
+ }
50
67
  </script>
68
+
51
69
  <template>
52
- <InputLabel
53
- :id="`${uuid}-label`"
54
- class="block"
55
- :for="uuid"
56
- :label="label"
57
- ></InputLabel>
58
- <input
59
- :id="uuid"
60
- :aria-labelledby="label ? `${uuid}-label` : undefined"
61
- :aria-describedby="help ? `${uuid}-help` : undefined"
62
- :class="[
63
- ...['mt-1', 'sm:text-sm'],
64
- ...(isTextType
65
- ? [
66
- 'block',
67
- 'shadow-sm',
68
- 'focus:xy-blue-500',
69
- 'focus:border-xy-blue ',
70
- 'border-gray-600',
71
- 'rounded-md',
72
- 'w-full',
73
- 'disabled:opacity-70',
74
- 'disabled:cursor-not-allowed',
75
- ]
76
- : []),
77
- ]"
78
- :placeholder="label"
79
- :type="type"
80
- :value="modelValue"
81
- v-bind="$attrs"
82
- @input="
83
- emit('update:modelValue', ($event.target as HTMLInputElement).value)
84
- "
85
- />
86
- <InputHelp :id="`${uuid}-help`" :text="help"></InputHelp>
70
+ <div>
71
+ <InputLabel
72
+ :id="`${inputID}-label`"
73
+ class="mb-2"
74
+ :for="inputID"
75
+ :label="label"
76
+ :required="isRequired"
77
+ />
78
+ <input
79
+ :id="inputID"
80
+ ref="targetInput"
81
+ :aria-labelledby="label ? `${inputID}-label` : undefined"
82
+ :aria-describedby="help ? `${inputID}-help` : undefined"
83
+ :class="[
84
+ 'block w-full rounded-md border-0 py-2 shadow-sm ring-1 ring-inset focus:ring-2 sm:text-sm sm:leading-6',
85
+ 'disabled:cursor-not-allowed disabled:bg-gray-50 disabled:text-gray-700 disabled:ring-gray-200',
86
+ errorState
87
+ ? 'text-red-900 ring-red-700 placeholder:text-red-300 focus:ring-red-700'
88
+ : 'text-gray-900 ring-gray-300 placeholder:text-gray-400 focus:ring-xy-blue-500',
89
+ ]"
90
+ :placeholder="placeholder"
91
+ :type="type"
92
+ :value="modelState"
93
+ v-bind="{ ...typeAttributes, ...$attrs }"
94
+ @input="onInput"
95
+ @invalid="onInvalid"
96
+ />
97
+ <InputHelp :id="`${inputID}-help`" class="mt-1" :text="help" />
98
+ <div v-if="errorState" class="mt-0.5">
99
+ <p class="text-sm text-red-700">{{ errorState }}</p>
100
+ </div>
101
+ </div>
87
102
  </template>
@@ -1,53 +1,69 @@
1
1
  <script setup lang="ts">
2
2
  import InputLabel from "./InputLabel.vue"
3
3
  import InputHelp from "./InputHelp.vue"
4
- import Uniques from "@/helpers/Uniques"
5
- import { useAttrs } from "vue"
4
+ import { useInputField, defaultInputProps } from "@/composables/forms"
5
+ import type { BooleanInput } from "@/composables/forms"
6
+ import { ref } from "vue"
6
7
 
7
- withDefaults(
8
- defineProps<{
9
- label?: string
10
- help?: string
11
- modelValue: boolean
12
- }>(),
13
- {
14
- label: "",
15
- help: "",
16
- }
17
- )
18
- const emits = defineEmits(["update:modelValue"])
19
- const attrs = useAttrs()
20
- const uuid = (attrs.id as string) || Uniques.CreateIdAttribute()
8
+ defineOptions({
9
+ inheritAttrs: false,
10
+ })
11
+
12
+ const props = withDefaults(defineProps<BooleanInput>(), defaultInputProps)
13
+
14
+ defineEmits(["update:modelValue", "update:error"])
15
+ const targetInput = ref<HTMLInputElement | null>(null)
16
+ const {
17
+ inputID,
18
+ isDisabled,
19
+ isRequired,
20
+ errorState,
21
+ modelState,
22
+ validate,
23
+ onInvalid,
24
+ } = useInputField({ props, targetInput })
25
+
26
+ const onChange = (e: Event) => {
27
+ modelState.value = (e.target as HTMLInputElement).checked
28
+ validate(e)
29
+ }
21
30
  </script>
31
+
22
32
  <template>
23
33
  <div class="relative flex items-start">
24
34
  <div class="flex items-center h-5">
25
35
  <input
26
- :id="uuid"
27
- :aria-labelledby="label ? `${uuid}-label` : undefined"
28
- :aria-describedby="help ? `${uuid}-help` : undefined"
29
- :checked="modelValue"
30
- class="focus:ring-xy-blue-500 h-4 w-4 text-xy-blue border-gray-600 rounded disabled:opacity-50 disabled:cursor-not-allowed"
36
+ :id="inputID"
37
+ ref="targetInput"
38
+ :aria-labelledby="label ? `${inputID}-label` : undefined"
39
+ :aria-describedby="help ? `${inputID}-help` : undefined"
40
+ :checked="modelState || undefined"
41
+ :class="[
42
+ 'h-4 w-4 rounded text-xy-blue cursor-pointer',
43
+ 'disabled:bg-gray-100 disabled:border-gray-200 disabled:cursor-not-allowed disabled:opacity-100',
44
+ 'checked:disabled:bg-xy-blue checked:disabled:border-xy-blue checked:disabled:opacity-50',
45
+ errorState
46
+ ? 'border-red-700 focus:ring-red-700'
47
+ : 'border-gray-300 focus:ring-xy-blue-500',
48
+ ]"
31
49
  type="checkbox"
32
- v-bind="{
33
- onChange: ($event) => {
34
- emits('update:modelValue', ($event.target as HTMLInputElement).checked)
35
- },
36
- ...$attrs,
37
- }"
50
+ v-bind="$attrs"
51
+ @change="onChange"
52
+ @invalid="onInvalid"
38
53
  />
39
54
  </div>
40
55
  <div class="ml-3">
41
56
  <InputLabel
42
- :id="`${uuid}-label`"
43
- class="mt-auto"
44
- :disabled="
45
- $attrs.hasOwnProperty('disabled') && $attrs.disabled !== false
46
- "
47
- :for="uuid"
57
+ :id="`${inputID}-label`"
58
+ :for="inputID"
48
59
  :label="label"
60
+ :class="isDisabled ? 'cursor-not-allowed' : 'cursor-pointer'"
61
+ :required="isRequired"
49
62
  />
50
- <InputHelp :id="`${uuid}-help`" class="-mt-1" :text="help"></InputHelp>
63
+ <InputHelp :id="`${inputID}-help`" :text="help"></InputHelp>
64
+ <div v-if="errorState" class="mt-0.5">
65
+ <p class="text-sm text-red-700">{{ errorState }}</p>
66
+ </div>
51
67
  </div>
52
68
  </div>
53
69
  </template>
@@ -1,39 +1,38 @@
1
1
  <script setup lang="ts">
2
- import Uniques from "@/helpers/Uniques"
3
2
  import flatpickr from "flatpickr"
4
3
  import "flatpickr/dist/flatpickr.min.css"
5
- import { onMounted, useAttrs } from "vue"
6
- import BaseInput from "./BaseInput.vue"
4
+ import { onMounted, ref } from "vue"
5
+ import { defaultInputProps, useInputField } from "@/composables/forms"
6
+ import type { DateRangeInput } from "@/composables/forms"
7
7
 
8
- const props = withDefaults(
9
- defineProps<{
10
- modelValue: {
11
- minDate: number
12
- maxDate: number
8
+ defineOptions({
9
+ inheritAttrs: false,
10
+ })
11
+
12
+ const props = withDefaults(defineProps<DateRangeInput>(), {
13
+ ...defaultInputProps,
14
+ maxRange: 0,
15
+ modelValue: () => {
16
+ return {
17
+ maxDate: 0,
18
+ minDate: 0,
13
19
  }
14
- maxRange?: number
15
- startDate?: number
16
- label?: string
17
- help?: string
18
- }>(),
19
- {
20
- maxRange: 0,
21
- startDate: 0,
22
- label: "",
23
- help: "",
24
- }
25
- )
20
+ },
21
+ placeholder: "mm-dd-yyyy range",
22
+ startDate: 0,
23
+ })
26
24
 
27
- const emits = defineEmits(["update:modelValue"])
28
- const attrs = useAttrs()
29
- const uuid = (attrs.id as string) || Uniques.CreateIdAttribute()
25
+ const targetInput = ref<HTMLInputElement | null>(null)
26
+ const { errorState, modelState, inputID, isRequired, onInvalid, validate } =
27
+ useInputField({ props, targetInput })
30
28
 
31
29
  const updateModelValue = (value: { minDate: number; maxDate: number }) => {
32
- emits("update:modelValue", value)
30
+ modelState.value = value
33
31
  }
34
32
 
35
33
  onMounted(() => {
36
34
  const opts: flatpickr.Options.Options = {
35
+ allowInput: true,
37
36
  dateFormat: "m-d-Y",
38
37
  mode: "range",
39
38
  maxDate: new Date(), // So far, we cannot have options past today for ranges
@@ -84,15 +83,40 @@ onMounted(() => {
84
83
  }
85
84
  }
86
85
 
87
- flatpickr(`#${uuid}`, opts)
86
+ flatpickr(`#${inputID.value}`, opts)
88
87
  })
89
88
  </script>
89
+
90
90
  <template>
91
- <BaseInput
92
- :id="uuid"
93
- type="text"
94
- placeholder="mm-dd-yyyy range"
95
- :label="label"
96
- :help="help"
97
- ></BaseInput>
91
+ <div>
92
+ <InputLabel
93
+ :id="`${inputID}-label`"
94
+ class="mb-2"
95
+ :for="inputID"
96
+ :label="label"
97
+ :required="isRequired"
98
+ />
99
+ <input
100
+ :id="inputID"
101
+ ref="targetInput"
102
+ :aria-labelledby="label ? `${inputID}-label` : undefined"
103
+ :aria-describedby="help ? `${inputID}-help` : undefined"
104
+ :class="[
105
+ 'block w-full rounded-md border-0 py-2 shadow-sm ring-1 ring-inset focus:ring-2 sm:text-sm sm:leading-6',
106
+ 'disabled:cursor-not-allowed disabled:bg-gray-50 disabled:text-gray-700 disabled:ring-gray-200',
107
+ errorState
108
+ ? 'text-red-900 ring-red-700 placeholder:text-red-300 focus:ring-red-700'
109
+ : 'text-gray-900 ring-gray-300 placeholder:text-gray-400 focus:ring-xy-blue-500',
110
+ ]"
111
+ :placeholder="placeholder"
112
+ v-bind="$attrs"
113
+ type="text"
114
+ @input="validate"
115
+ @invalid="onInvalid"
116
+ />
117
+ <InputHelp :id="`${inputID}-help`" class="mt-1" :text="help" />
118
+ <div v-if="errorState" class="mt-0.5">
119
+ <p class="text-sm text-red-700">{{ errorState }}</p>
120
+ </div>
121
+ </div>
98
122
  </template>
@@ -1,23 +1,43 @@
1
1
  <script setup lang="ts">
2
- import { hasSlotContent } from "@/helpers/Slots"
3
- withDefaults(
2
+ import { computed } from "vue"
3
+
4
+ const props = withDefaults(
4
5
  defineProps<{
6
+ label?: string
7
+ required?: boolean
5
8
  tag?: string
6
9
  }>(),
7
10
  {
11
+ label: "",
12
+ required: false,
8
13
  tag: "legend",
9
14
  }
10
15
  )
16
+
17
+ const labelDisplay = computed((): string => {
18
+ if (!props.required) {
19
+ return props.label
20
+ }
21
+
22
+ // remove trailing * on the existing label
23
+ const regex = /\*$/gm
24
+
25
+ if (regex.exec(props.label) !== null) {
26
+ return props.label.substring(0, props.label.length - 1)
27
+ }
28
+
29
+ return props.label
30
+ })
11
31
  </script>
32
+
12
33
  <template>
13
34
  <component
14
35
  :is="tag"
15
- v-if="hasSlotContent($slots.default)"
16
- v-bind="{
17
- ...$attrs,
18
- class: 'text-sm font-semibold leading-snug text-gray-900',
19
- }"
36
+ v-if="label"
37
+ class="block text-base leading-snug font-medium text-gray-800"
38
+ v-bind="$attrs"
20
39
  >
21
- <slot />
40
+ {{ labelDisplay }}
41
+ <span v-if="props.required" class="text-red-700/80">*</span>
22
42
  </component>
23
43
  </template>
@@ -14,10 +14,8 @@ withDefaults(
14
14
  <component
15
15
  :is="tag"
16
16
  v-if="text"
17
- v-bind="{
18
- ...$attrs,
19
- class: 'mt-1 text-sm leading-snug font-normal text-gray-700',
20
- }"
17
+ class="text-sm leading-snug font-normal text-gray-600"
18
+ v-bind="$attrs"
21
19
  >
22
20
  {{ text }}
23
21
  </component>
@@ -1,28 +1,43 @@
1
1
  <script setup lang="ts">
2
- withDefaults(
2
+ import { computed } from "vue"
3
+
4
+ const props = withDefaults(
3
5
  defineProps<{
4
- disabled?: boolean
5
6
  label?: string
7
+ required?: boolean
6
8
  tag?: string
7
9
  }>(),
8
10
  {
9
- disabled: false,
10
11
  label: "",
12
+ required: false,
11
13
  tag: "label",
12
14
  }
13
15
  )
16
+
17
+ const labelDisplay = computed((): string => {
18
+ if (!props.required) {
19
+ return props.label
20
+ }
21
+
22
+ // remove trailing * on the existing label
23
+ const regex = /\*$/gm
24
+
25
+ if (regex.exec(props.label) !== null) {
26
+ return props.label.substring(0, props.label.length - 1)
27
+ }
28
+
29
+ return props.label
30
+ })
14
31
  </script>
32
+
15
33
  <template>
16
34
  <component
17
35
  :is="tag"
18
- v-if="label"
19
- :class="{
20
- 'block my-1 text-sm font-semibold leading-snug text-gray-900': true,
21
- 'opacity-75': disabled,
22
- }"
23
- v-bind="{
24
- ...$attrs,
25
- }"
26
- >{{ label }}</component
36
+ v-if="labelDisplay"
37
+ class="block text-sm leading-snug font-medium text-gray-800"
38
+ v-bind="$attrs"
27
39
  >
40
+ {{ labelDisplay }}
41
+ <span v-if="props.required" class="text-red-700/80">*</span>
42
+ </component>
28
43
  </template>