@xy-planning-network/trees 0.7.5-rc2 → 0.7.5-rc3

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 (29) hide show
  1. package/dist/trees.es.js +3084 -2890
  2. package/dist/trees.umd.js +10 -10
  3. package/package.json +1 -1
  4. package/src/lib-components/forms/BaseInput.vue +7 -12
  5. package/src/lib-components/forms/Checkbox.vue +9 -11
  6. package/src/lib-components/forms/DateRangePicker.vue +19 -12
  7. package/src/lib-components/forms/InputError.vue +14 -0
  8. package/src/lib-components/forms/MultiCheckboxes.vue +15 -13
  9. package/src/lib-components/forms/Radio.vue +10 -21
  10. package/src/lib-components/forms/RadioCards.vue +5 -18
  11. package/src/lib-components/forms/Select.vue +16 -12
  12. package/src/lib-components/forms/TextArea.vue +9 -11
  13. package/src/lib-components/forms/YesOrNoRadio.vue +10 -12
  14. package/src/lib-components/indicators/InlineAlert.vue +194 -0
  15. package/types/composables/forms.d.ts +8 -28
  16. package/types/composables/setupHelpers.d.ts +15 -0
  17. package/types/entry.d.ts +7 -0
  18. package/types/lib-components/forms/BaseInput.vue.d.ts +0 -9
  19. package/types/lib-components/forms/Checkbox.vue.d.ts +0 -9
  20. package/types/lib-components/forms/DateRangePicker.vue.d.ts +0 -9
  21. package/types/lib-components/forms/InputError.vue.d.ts +14 -0
  22. package/types/lib-components/forms/MultiCheckboxes.vue.d.ts +0 -9
  23. package/types/lib-components/forms/Radio.vue.d.ts +0 -9
  24. package/types/lib-components/forms/RadioCards.vue.d.ts +0 -3
  25. package/types/lib-components/forms/Select.vue.d.ts +0 -9
  26. package/types/lib-components/forms/TextArea.vue.d.ts +0 -9
  27. package/types/lib-components/forms/YesOrNoRadio.vue.d.ts +0 -9
  28. package/types/lib-components/index.d.ts +5 -2
  29. package/types/lib-components/indicators/InlineAlert.vue.d.ts +83 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xy-planning-network/trees",
3
- "version": "0.7.5-rc2",
3
+ "version": "0.7.5-rc3",
4
4
  "description": "",
5
5
  "license": "MIT",
6
6
  "repository": "github:xy-planning-network/trees",
@@ -1,12 +1,12 @@
1
1
  <script setup lang="ts">
2
2
  import InputLabel from "./InputLabel.vue"
3
3
  import InputHelp from "./InputHelp.vue"
4
+ import InputError from "./InputError.vue"
4
5
  import {
5
6
  useInputField,
6
7
  defaultInputProps,
7
8
  emailPattern,
8
9
  looseToNumber,
9
- passwordPattern,
10
10
  phonePattern,
11
11
  } from "@/composables/forms"
12
12
  import type { TextLikeInput } from "@/composables/forms"
@@ -19,7 +19,7 @@ defineOptions({
19
19
  const props = withDefaults(defineProps<TextLikeInput>(), defaultInputProps)
20
20
 
21
21
  defineEmits(["update:modelValue", "update:error"])
22
- const targetInput = ref<HTMLInputElement | null>(null)
22
+ const input = ref<HTMLInputElement | null>(null)
23
23
  const {
24
24
  errorState,
25
25
  modelState,
@@ -27,12 +27,12 @@ const {
27
27
  isRequired,
28
28
  onInvalid,
29
29
  inputValidation,
30
- } = useInputField({ props, targetInput })
30
+ } = useInputField(props)
31
31
 
32
32
  // A wrapper component may need to have direct access
33
33
  // to the underlying HTMLInputElement that BaseInput binds to
34
34
  // example: GoogleMaps Autocomplete inputs
35
- defineExpose({ input: targetInput })
35
+ defineExpose({ input: input })
36
36
 
37
37
  const typeAttributes = computed(() => {
38
38
  switch (props.type) {
@@ -45,10 +45,6 @@ const typeAttributes = computed(() => {
45
45
  return {
46
46
  pattern: emailPattern,
47
47
  }
48
- case "password":
49
- return {
50
- pattern: passwordPattern,
51
- }
52
48
  case "tel":
53
49
  return {
54
50
  pattern: phonePattern,
@@ -82,9 +78,10 @@ const onInput = (e: Event) => {
82
78
  />
83
79
  <input
84
80
  :id="inputID"
85
- ref="targetInput"
81
+ ref="input"
86
82
  :aria-labelledby="label ? `${inputID}-label` : undefined"
87
83
  :aria-describedby="help ? `${inputID}-help` : undefined"
84
+ :aria-errormessage="errorState ? `${inputID}-error` : undefined"
88
85
  :class="[
89
86
  'block w-full rounded-md border-0 py-2 shadow-sm ring-1 ring-inset focus:ring-2 sm:text-sm sm:leading-6',
90
87
  'disabled:cursor-not-allowed disabled:bg-gray-50 disabled:text-gray-700 disabled:ring-gray-200',
@@ -100,8 +97,6 @@ const onInput = (e: Event) => {
100
97
  @invalid="onInvalid"
101
98
  />
102
99
  <InputHelp :id="`${inputID}-help`" class="mt-1" :text="help" />
103
- <div v-if="errorState" class="mt-0.5">
104
- <p class="text-sm text-red-700">{{ errorState }}</p>
105
- </div>
100
+ <InputError :id="`${inputID}-error`" class="mt-0.5" :text="errorState" />
106
101
  </div>
107
102
  </template>
@@ -1,9 +1,9 @@
1
1
  <script setup lang="ts">
2
2
  import InputLabel from "./InputLabel.vue"
3
3
  import InputHelp from "./InputHelp.vue"
4
+ import InputError from "./InputError.vue"
4
5
  import { useInputField, defaultInputProps } from "@/composables/forms"
5
6
  import type { BooleanInput } from "@/composables/forms"
6
- import { ref } from "vue"
7
7
 
8
8
  defineOptions({
9
9
  inheritAttrs: false,
@@ -12,8 +12,8 @@ defineOptions({
12
12
  const props = withDefaults(defineProps<BooleanInput>(), defaultInputProps)
13
13
 
14
14
  defineEmits(["update:modelValue", "update:error"])
15
- const targetInput = ref<HTMLInputElement | null>(null)
16
15
  const {
16
+ aria,
17
17
  inputID,
18
18
  isDisabled,
19
19
  isRequired,
@@ -21,7 +21,7 @@ const {
21
21
  modelState,
22
22
  validate,
23
23
  onInvalid,
24
- } = useInputField({ props, targetInput })
24
+ } = useInputField(props)
25
25
 
26
26
  const onChange = (e: Event) => {
27
27
  modelState.value = (e.target as HTMLInputElement).checked
@@ -34,9 +34,9 @@ const onChange = (e: Event) => {
34
34
  <div class="flex items-center h-5">
35
35
  <input
36
36
  :id="inputID"
37
- ref="targetInput"
38
- :aria-labelledby="label ? `${inputID}-label` : undefined"
39
- :aria-describedby="help ? `${inputID}-help` : undefined"
37
+ :aria-labelledby="aria.labelledby"
38
+ :aria-describedby="aria.describedby"
39
+ :aria-errormessage="aria.errormessage"
40
40
  :checked="modelState || undefined"
41
41
  :class="[
42
42
  'h-4 w-4 rounded text-xy-blue cursor-pointer',
@@ -54,16 +54,14 @@ const onChange = (e: Event) => {
54
54
  </div>
55
55
  <div class="ml-3">
56
56
  <InputLabel
57
- :id="`${inputID}-label`"
57
+ :id="aria.labelledby"
58
58
  :for="inputID"
59
59
  :label="label"
60
60
  :class="isDisabled ? 'cursor-not-allowed' : 'cursor-pointer'"
61
61
  :required="isRequired"
62
62
  />
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>
63
+ <InputHelp :id="aria.describedby" :text="help"></InputHelp>
64
+ <InputError :id="aria.errormessage" class="mt-0.5" :text="errorState" />
67
65
  </div>
68
66
  </div>
69
67
  </template>
@@ -1,7 +1,10 @@
1
1
  <script setup lang="ts">
2
+ import InputLabel from "./InputLabel.vue"
3
+ import InputHelp from "./InputHelp.vue"
4
+ import InputError from "./InputError.vue"
2
5
  import flatpickr from "flatpickr"
3
6
  import "flatpickr/dist/flatpickr.min.css"
4
- import { onMounted, ref } from "vue"
7
+ import { onMounted } from "vue"
5
8
  import { defaultInputProps, useInputField } from "@/composables/forms"
6
9
  import type { DateRangeInput } from "@/composables/forms"
7
10
 
@@ -22,9 +25,15 @@ const props = withDefaults(defineProps<DateRangeInput>(), {
22
25
  startDate: 0,
23
26
  })
24
27
 
25
- const targetInput = ref<HTMLInputElement | null>(null)
26
- const { errorState, modelState, inputID, isRequired, onInvalid, validate } =
27
- useInputField({ props, targetInput })
28
+ const {
29
+ aria,
30
+ errorState,
31
+ modelState,
32
+ inputID,
33
+ isRequired,
34
+ onInvalid,
35
+ validate,
36
+ } = useInputField(props)
28
37
 
29
38
  const updateModelValue = (value: { minDate: number; maxDate: number }) => {
30
39
  modelState.value = value
@@ -90,7 +99,7 @@ onMounted(() => {
90
99
  <template>
91
100
  <div>
92
101
  <InputLabel
93
- :id="`${inputID}-label`"
102
+ :id="aria.labelledby"
94
103
  class="mb-2"
95
104
  :for="inputID"
96
105
  :label="label"
@@ -98,9 +107,9 @@ onMounted(() => {
98
107
  />
99
108
  <input
100
109
  :id="inputID"
101
- ref="targetInput"
102
- :aria-labelledby="label ? `${inputID}-label` : undefined"
103
- :aria-describedby="help ? `${inputID}-help` : undefined"
110
+ :aria-labelledby="aria.labelledby"
111
+ :aria-describedby="aria.describedby"
112
+ :aria-errormessage="aria.errormessage"
104
113
  :class="[
105
114
  '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
115
  'disabled:cursor-not-allowed disabled:bg-gray-50 disabled:text-gray-700 disabled:ring-gray-200',
@@ -114,9 +123,7 @@ onMounted(() => {
114
123
  @input="validate"
115
124
  @invalid="onInvalid"
116
125
  />
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>
126
+ <InputHelp :id="aria.describedby" class="mt-1" :text="help" />
127
+ <InputError :id="aria.errormessage" class="mt-0.5" :text="errorState" />
121
128
  </div>
122
129
  </template>
@@ -0,0 +1,14 @@
1
+ <script setup lang="ts">
2
+ withDefaults(
3
+ defineProps<{
4
+ text?: string
5
+ }>(),
6
+ {
7
+ text: "",
8
+ }
9
+ )
10
+ </script>
11
+
12
+ <template>
13
+ <p v-if="text" class="text-sm text-red-700">{{ text }}</p>
14
+ </template>
@@ -2,6 +2,7 @@
2
2
  import FieldsetLegend from "./FieldsetLegend.vue"
3
3
  import InputLabel from "./InputLabel.vue"
4
4
  import InputHelp from "./InputHelp.vue"
5
+ import InputError from "./InputError.vue"
5
6
  import { useInputField, defaultInputProps } from "@/composables/forms"
6
7
  import type { MultiChoiceInput, ColumnedInput } from "@/composables/forms"
7
8
  import { computed, ref } from "vue"
@@ -16,10 +17,8 @@ const props = withDefaults(
16
17
  )
17
18
 
18
19
  defineEmits(["update:modelValue", "update:error"])
19
- const targetInput = ref<HTMLInputElement | null>(null)
20
- const { inputID, isDisabled, modelState, errorState, validate } = useInputField(
21
- { props, targetInput }
22
- )
20
+ const { aria, inputID, isDisabled, modelState, errorState, validate } =
21
+ useInputField(props)
23
22
 
24
23
  const onChange = (e: Event, val: string | number) => {
25
24
  const checked = (e.target as HTMLInputElement).checked
@@ -74,9 +73,12 @@ const countError = computed(() => {
74
73
  return ""
75
74
  })
76
75
 
76
+ const errorInput = ref<HTMLInputElement | null>(null)
77
77
  const setValidationError = () => {
78
78
  if (!errorState.value) {
79
79
  errorState.value = countError.value
80
+ // ensure the browser tooltip contains our error message
81
+ errorInput.value?.setCustomValidity(countError.value)
80
82
  }
81
83
  }
82
84
  </script>
@@ -84,25 +86,25 @@ const setValidationError = () => {
84
86
  <template>
85
87
  <fieldset
86
88
  class="relative space-y-4"
87
- :aria-labelledby="label ? `${inputID}-legend` : undefined"
88
- :aria-describedby="help ? `${inputID}-help` : undefined"
89
+ :aria-labelledby="aria.labelledby"
90
+ :aria-describedby="aria.describedby"
91
+ :aria-errormessage="aria.errormessage"
89
92
  >
90
93
  <div v-if="label">
91
94
  <FieldsetLegend
92
- :id="`${inputID}-legend`"
95
+ :id="aria.labelledby"
93
96
  :label="label"
94
97
  :required="minCount > 0"
95
98
  />
96
- <InputHelp v-if="help" :id="`${inputID}-help`" tag="p" :text="help" />
99
+ <InputHelp v-if="help" :id="aria.describedby" tag="p" :text="help" />
97
100
  </div>
98
101
 
99
- <div v-if="errorState" class="mt-0.5">
100
- <p class="text-sm text-red-700">{{ errorState }}</p>
101
- </div>
102
+ <InputError :id="aria.errormessage" :text="errorState" />
102
103
 
104
+ <!--Hidden input for custom validation-->
103
105
  <input
104
- v-if="countError || errorState"
105
- ref="targetInput"
106
+ v-if="countError"
107
+ ref="errorInput"
106
108
  required
107
109
  class="sr-only top-1 left-1"
108
110
  aria-hidden
@@ -1,8 +1,8 @@
1
1
  <script setup lang="ts">
2
- import { computed, ref } from "vue"
3
2
  import FieldsetLegend from "./FieldsetLegend.vue"
4
- import InputHelp from "./InputHelp.vue"
5
3
  import InputLabel from "./InputLabel.vue"
4
+ import InputHelp from "./InputHelp.vue"
5
+ import InputError from "./InputError.vue"
6
6
  import { useInputField, defaultInputProps } from "@/composables/forms"
7
7
  import type { OptionsInput, ColumnedInput } from "@/composables/forms"
8
8
 
@@ -17,17 +17,8 @@ const props = withDefaults(
17
17
 
18
18
  defineEmits(["update:modelValue", "update:error"])
19
19
 
20
- const radios = ref<HTMLInputElement[]>([])
21
- // there are multiple radio buttons that could be the target
22
- // for validation set to the first input
23
- const targetInput = computed(() => {
24
- if (radios.value.length === 0) {
25
- return null
26
- }
27
-
28
- return radios.value[0]
29
- })
30
20
  const {
21
+ aria,
31
22
  errorState,
32
23
  modelState,
33
24
  inputID,
@@ -35,7 +26,7 @@ const {
35
26
  isRequired,
36
27
  onInvalid,
37
28
  validate,
38
- } = useInputField({ props, targetInput })
29
+ } = useInputField(props)
39
30
 
40
31
  const onChange = (e: Event, val: string | number) => {
41
32
  modelState.value = val
@@ -46,21 +37,20 @@ const onChange = (e: Event, val: string | number) => {
46
37
  <template>
47
38
  <fieldset
48
39
  class="space-y-4"
49
- :aria-labelledby="label ? `${inputID}-legend` : undefined"
50
- :aria-describedby="help ? `${inputID}-help` : undefined"
40
+ :aria-labelledby="aria.labelledby"
41
+ :aria-describedby="aria.describedby"
42
+ :aria-errormessage="aria.errormessage"
51
43
  >
52
44
  <div v-if="label">
53
45
  <FieldsetLegend
54
- :id="`${inputID}-legend`"
46
+ :id="aria.labelledby"
55
47
  :label="label"
56
48
  :required="isRequired"
57
49
  />
58
- <InputHelp v-if="help" :id="`${inputID}-help`" tag="p" :text="help" />
50
+ <InputHelp v-if="help" :id="aria.describedby" tag="p" :text="help" />
59
51
  </div>
60
52
 
61
- <div v-if="errorState" class="mt-0.5">
62
- <p class="text-sm text-red-700">{{ errorState }}</p>
63
- </div>
53
+ <InputError :id="aria.errormessage" :text="errorState" />
64
54
 
65
55
  <div class="flex">
66
56
  <div
@@ -79,7 +69,6 @@ const onChange = (e: Event, val: string | number) => {
79
69
  <div class="flex items-center h-5">
80
70
  <input
81
71
  :id="`${inputID}-${index}`"
82
- ref="radios"
83
72
  :aria-describedby="
84
73
  option.help ? `${inputID}-${index}-help` : undefined
85
74
  "
@@ -8,6 +8,7 @@ import {
8
8
  import { CheckCircleIcon } from "@heroicons/vue/solid"
9
9
  import InputLabel from "./InputLabel.vue"
10
10
  import InputHelp from "./InputHelp.vue"
11
+ import InputError from "./InputError.vue"
11
12
  import FieldsetLegend from "./FieldsetLegend.vue"
12
13
  import { defaultInputProps, useInputField } from "@/composables/forms"
13
14
  import type {
@@ -15,7 +16,6 @@ import type {
15
16
  InputOption,
16
17
  OptionsInput,
17
18
  } from "@/composables/forms"
18
- import { computed, ref } from "vue"
19
19
 
20
20
  defineOptions({
21
21
  inheritAttrs: false,
@@ -37,25 +37,15 @@ const props = withDefaults(
37
37
  )
38
38
 
39
39
  defineEmits(["update:modelValue", "update:error"])
40
- const hiddenRadios = ref<HTMLInputElement[]>([])
41
- // there are multiple radio buttons that could be the target
42
- // for validation set to the first input
43
- const targetInput = computed(() => {
44
- if (hiddenRadios.value.length === 0) {
45
- return null
46
- }
47
-
48
- return hiddenRadios.value[0]
49
- })
50
40
  const {
51
- inputID,
41
+ aria,
52
42
  isDisabled,
53
43
  isRequired,
54
44
  nameAttr,
55
45
  modelState,
56
46
  errorState,
57
47
  onInvalid,
58
- } = useInputField({ props, targetInput })
48
+ } = useInputField(props)
59
49
 
60
50
  const onUpdate = (val: unknown) => {
61
51
  if (val) {
@@ -69,7 +59,7 @@ const onUpdate = (val: unknown) => {
69
59
  v-model="modelState"
70
60
  :disabled="isDisabled"
71
61
  :aria-invalid="errorState ? 'true' : null"
72
- :aria-errormessage="errorState ? `error-${inputID}` : null"
62
+ :aria-errormessage="aria.errormessage"
73
63
  @update:model-value="onUpdate"
74
64
  >
75
65
  <RadioGroupLabel v-if="label" class="block">
@@ -80,9 +70,7 @@ const onUpdate = (val: unknown) => {
80
70
  <InputHelp :text="help" />
81
71
  </RadioGroupDescription>
82
72
 
83
- <div v-if="errorState" :id="`error-${inputID}`">
84
- <p class="text-sm text-red-700">{{ errorState }}</p>
85
- </div>
73
+ <InputError :id="aria.errormessage" :text="errorState" />
86
74
 
87
75
  <div
88
76
  class="mt-4 grid grid-cols-1 gap-y-5 gap-x-4 relative"
@@ -167,7 +155,6 @@ const onUpdate = (val: unknown) => {
167
155
 
168
156
  <!--TODO: (spk) ideally this would trigger a change event -->
169
157
  <input
170
- ref="hiddenRadios"
171
158
  class="sr-only top-1 left-1"
172
159
  aria-hidden="true"
173
160
  :checked="checked"
@@ -1,9 +1,9 @@
1
1
  <script setup lang="ts">
2
2
  import InputLabel from "./InputLabel.vue"
3
3
  import InputHelp from "./InputHelp.vue"
4
+ import InputError from "./InputError.vue"
4
5
  import { defaultInputProps, useInputField } from "@/composables/forms"
5
6
  import type { OptionsInput } from "@/composables/forms"
6
- import { ref } from "vue"
7
7
 
8
8
  defineOptions({
9
9
  inheritAttrs: false,
@@ -15,9 +15,15 @@ const props = withDefaults(defineProps<OptionsInput>(), {
15
15
  })
16
16
 
17
17
  defineEmits(["update:modelValue", "update:error"])
18
- const targetInput = ref<HTMLInputElement | null>(null)
19
- const { inputID, isRequired, validate, modelState, errorState, onInvalid } =
20
- useInputField({ props, targetInput })
18
+ const {
19
+ aria,
20
+ inputID,
21
+ isRequired,
22
+ validate,
23
+ modelState,
24
+ errorState,
25
+ onInvalid,
26
+ } = useInputField(props)
21
27
 
22
28
  const onChange = (e: Event) => {
23
29
  modelState.value = (e.target as HTMLInputElement).value
@@ -28,7 +34,7 @@ const onChange = (e: Event) => {
28
34
  <template>
29
35
  <div>
30
36
  <InputLabel
31
- :id="`${inputID}-label`"
37
+ :id="aria.labelledby"
32
38
  class="mb-2"
33
39
  :for="inputID"
34
40
  :label="label"
@@ -36,9 +42,9 @@ const onChange = (e: Event) => {
36
42
  />
37
43
  <select
38
44
  :id="inputID"
39
- ref="targetInput"
40
- :aria-labelledby="label ? `${inputID}-label` : undefined"
41
- :aria-describedby="help ? `${inputID}-help` : undefined"
45
+ :aria-labelledby="aria.labelledby"
46
+ :aria-describedby="aria.describedby"
47
+ :aria-errormessage="aria.errormessage"
42
48
  :class="[
43
49
  'block w-full rounded-md border-0 py-2 shadow-sm ring-1 ring-inset focus:ring-2 sm:text-sm sm:leading-6 pl-3 pr-10',
44
50
  'disabled:cursor-not-allowed disabled:bg-gray-50 disabled:text-gray-700 disabled:ring-gray-200 disabled:opacity-100',
@@ -60,9 +66,7 @@ const onChange = (e: Event) => {
60
66
  v-text="option.label"
61
67
  />
62
68
  </select>
63
- <InputHelp :id="`${inputID}-help`" class="mt-1" :text="help" />
64
- <div v-if="errorState" class="mt-0.5">
65
- <p class="text-sm text-red-700">{{ errorState }}</p>
66
- </div>
69
+ <InputHelp :id="aria.describedby" class="mt-1" :text="help" />
70
+ <InputError :id="aria.errormessage" class="mt-0.5" :text="errorState" />
67
71
  </div>
68
72
  </template>
@@ -1,9 +1,9 @@
1
1
  <script setup lang="ts">
2
2
  import InputLabel from "./InputLabel.vue"
3
3
  import InputHelp from "./InputHelp.vue"
4
+ import InputError from "./InputError.vue"
4
5
  import { useInputField, defaultInputProps } from "@/composables/forms"
5
6
  import type { TextareaInput } from "@/composables/forms"
6
- import { ref } from "vue"
7
7
 
8
8
  defineOptions({
9
9
  inheritAttrs: false,
@@ -11,15 +11,15 @@ defineOptions({
11
11
 
12
12
  const props = withDefaults(defineProps<TextareaInput>(), defaultInputProps)
13
13
  defineEmits(["update:modelValue", "update:error"])
14
- const targetInput = ref<HTMLInputElement | null>(null)
15
14
  const {
15
+ aria,
16
16
  inputID,
17
17
  isRequired,
18
18
  modelState,
19
19
  errorState,
20
20
  onInvalid,
21
21
  inputValidation,
22
- } = useInputField({ props, targetInput })
22
+ } = useInputField(props)
23
23
 
24
24
  const onInput = (e: Event) => {
25
25
  modelState.value = (e.target as HTMLInputElement).value
@@ -30,7 +30,7 @@ const onInput = (e: Event) => {
30
30
  <template>
31
31
  <div>
32
32
  <InputLabel
33
- :id="`${inputID}-label`"
33
+ :id="aria.labelledby"
34
34
  class="mb-2"
35
35
  :for="inputID"
36
36
  :label="label"
@@ -38,9 +38,9 @@ const onInput = (e: Event) => {
38
38
  />
39
39
  <textarea
40
40
  :id="inputID"
41
- ref="targetInput"
42
- :aria-labelledby="label ? `${inputID}-label` : undefined"
43
- :aria-describedby="help ? `${inputID}-help` : undefined"
41
+ :aria-labelledby="aria.labelledby"
42
+ :aria-describedby="aria.describedby"
43
+ :aria-errormessage="aria.errormessage"
44
44
  :class="[
45
45
  'block w-full rounded-md border-0 py-2 shadow-sm ring-1 ring-inset focus:ring-2 sm:text-sm sm:leading-6',
46
46
  'disabled:cursor-not-allowed disabled:bg-gray-50 disabled:text-gray-700 disabled:ring-gray-200',
@@ -53,9 +53,7 @@ const onInput = (e: Event) => {
53
53
  @input="onInput"
54
54
  @invalid="onInvalid"
55
55
  />
56
- <InputHelp :id="`${inputID}-help`" class="mb-1" :text="help"></InputHelp>
57
- <div v-if="errorState" class="mt-0.5">
58
- <p class="text-sm text-red-700">{{ errorState }}</p>
59
- </div>
56
+ <InputHelp :id="aria.describedby" class="mb-1" :text="help"></InputHelp>
57
+ <InputError :id="aria.errormessage" class="mt-0.5" :text="errorState" />
60
58
  </div>
61
59
  </template>
@@ -1,9 +1,9 @@
1
1
  <script setup lang="ts">
2
2
  import InputLabel from "./InputLabel.vue"
3
3
  import InputHelp from "./InputHelp.vue"
4
+ import InputError from "./InputError.vue"
4
5
  import { useInputField, defaultInputProps } from "@/composables/forms"
5
6
  import type { BooleanInput } from "@/composables/forms"
6
- import { ref } from "vue"
7
7
 
8
8
  defineOptions({
9
9
  inheritAttrs: false,
@@ -12,9 +12,8 @@ defineOptions({
12
12
  const props = withDefaults(defineProps<BooleanInput>(), defaultInputProps)
13
13
 
14
14
  defineEmits(["update:modelValue", "update:error"])
15
- const targetInput = ref<HTMLInputElement | null>(null)
16
15
  const {
17
- inputID,
16
+ aria,
18
17
  isDisabled,
19
18
  isRequired,
20
19
  nameAttr,
@@ -22,7 +21,7 @@ const {
22
21
  errorState,
23
22
  onInvalid,
24
23
  validate,
25
- } = useInputField({ props, targetInput })
24
+ } = useInputField(props)
26
25
 
27
26
  const onChange = (e: Event, val: boolean) => {
28
27
  modelState.value = val
@@ -33,22 +32,22 @@ const onChange = (e: Event, val: boolean) => {
33
32
  <template>
34
33
  <fieldset
35
34
  class="space-y-4"
36
- :aria-labelledby="label ? `${inputID}-legend` : undefined"
37
- :aria-describedby="help ? `${inputID}-help` : undefined"
35
+ :aria-labelledby="aria.labelledby"
36
+ :aria-describedby="aria.describedby"
37
+ :aria-errormessage="aria.errormessage"
38
38
  >
39
39
  <div v-if="label">
40
40
  <InputLabel
41
+ :id="aria.labelledby"
41
42
  class="block my-auto"
42
43
  :label="label"
43
- tag="legend"
44
44
  :required="isRequired"
45
+ tag="legend"
45
46
  />
46
- <InputHelp v-if="help" :id="`${inputID}-help`" tag="p" :text="help" />
47
+ <InputHelp v-if="help" :id="aria.describedby" tag="p" :text="help" />
47
48
  </div>
48
49
 
49
- <div v-if="errorState" class="mt-0.5">
50
- <p class="text-sm text-red-700">{{ errorState }}</p>
51
- </div>
50
+ <InputError :id="aria.errormessage" :text="errorState" />
52
51
 
53
52
  <div>
54
53
  <label
@@ -58,7 +57,6 @@ const onChange = (e: Event, val: boolean) => {
58
57
  >
59
58
  <input
60
59
  :id="`${nameAttr}-true`"
61
- rel="targetInput"
62
60
  type="radio"
63
61
  :class="[
64
62
  'h-4 w-4 text-xy-blue cursor-pointer',