@soave/ui 0.2.1 → 0.3.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.
Files changed (109) hide show
  1. package/dist/adapters/types.d.ts +1 -1
  2. package/dist/components/Button.vue +36 -0
  3. package/dist/components/Card.vue +23 -0
  4. package/dist/components/Checkbox.vue +44 -0
  5. package/dist/components/Dialog.vue +99 -0
  6. package/dist/components/Input.vue +42 -0
  7. package/dist/components/RadioGroup.vue +35 -0
  8. package/dist/components/RadioItem.vue +55 -0
  9. package/dist/components/Select.vue +95 -0
  10. package/dist/components/SelectContent.vue +40 -0
  11. package/dist/components/SelectItem.vue +44 -0
  12. package/dist/components/SelectTrigger.vue +61 -0
  13. package/dist/components/Switch.vue +43 -0
  14. package/dist/components/Textarea.vue +49 -0
  15. package/dist/components/index.d.ts +13 -0
  16. package/dist/components/index.mjs +13 -0
  17. package/dist/composables/useButton.d.ts +2 -2
  18. package/dist/composables/useButton.mjs +14 -41
  19. package/dist/composables/useCard.d.ts +2 -2
  20. package/dist/composables/useCard.mjs +5 -18
  21. package/dist/composables/useCheckbox.d.ts +2 -1
  22. package/dist/composables/useCheckbox.mjs +11 -44
  23. package/dist/composables/useFileInput.d.ts +2 -0
  24. package/dist/composables/useFileInput.mjs +15 -30
  25. package/dist/composables/useForm.mjs +2 -2
  26. package/dist/composables/useInput.d.ts +2 -2
  27. package/dist/composables/useInput.mjs +12 -33
  28. package/dist/composables/useRadio.d.ts +2 -1
  29. package/dist/composables/useRadio.mjs +10 -42
  30. package/dist/composables/useSelect.d.ts +3 -0
  31. package/dist/composables/useSelect.mjs +20 -49
  32. package/dist/composables/useSwitch.d.ts +2 -1
  33. package/dist/composables/useSwitch.mjs +10 -43
  34. package/dist/composables/useTextarea.d.ts +2 -1
  35. package/dist/composables/useTextarea.mjs +12 -33
  36. package/dist/composables/useToast.mjs +1 -1
  37. package/dist/env.d.ts +11 -0
  38. package/dist/index.d.ts +1 -1
  39. package/dist/index.mjs +1 -1
  40. package/dist/types/button.d.ts +19 -5
  41. package/dist/types/card.d.ts +11 -2
  42. package/dist/types/checkbox.d.ts +15 -6
  43. package/dist/types/composables.d.ts +52 -24
  44. package/dist/types/config.d.ts +1 -0
  45. package/dist/types/file-input.d.ts +15 -5
  46. package/dist/types/form.d.ts +5 -5
  47. package/dist/types/input.d.ts +17 -6
  48. package/dist/types/radio.d.ts +14 -6
  49. package/dist/types/select.d.ts +25 -7
  50. package/dist/types/switch.d.ts +14 -5
  51. package/dist/types/textarea.d.ts +20 -7
  52. package/dist/types/toast.d.ts +2 -2
  53. package/dist/types/tooltip.d.ts +3 -0
  54. package/dist/types/tooltip.mjs +1 -0
  55. package/package.json +1 -9
  56. package/dist/components/ui/Alert.vue +0 -41
  57. package/dist/components/ui/AlertDescription.vue +0 -22
  58. package/dist/components/ui/AlertTitle.vue +0 -22
  59. package/dist/components/ui/Button.vue +0 -85
  60. package/dist/components/ui/Card.vue +0 -39
  61. package/dist/components/ui/CardContent.vue +0 -22
  62. package/dist/components/ui/CardDescription.vue +0 -22
  63. package/dist/components/ui/CardFooter.vue +0 -22
  64. package/dist/components/ui/CardHeader.vue +0 -22
  65. package/dist/components/ui/CardTitle.vue +0 -22
  66. package/dist/components/ui/Checkbox.vue +0 -94
  67. package/dist/components/ui/Dialog.vue +0 -110
  68. package/dist/components/ui/DialogDescription.vue +0 -22
  69. package/dist/components/ui/DialogFooter.vue +0 -22
  70. package/dist/components/ui/DialogHeader.vue +0 -22
  71. package/dist/components/ui/DialogTitle.vue +0 -22
  72. package/dist/components/ui/DropdownMenu.vue +0 -32
  73. package/dist/components/ui/DropdownMenuContent.vue +0 -69
  74. package/dist/components/ui/DropdownMenuItem.vue +0 -71
  75. package/dist/components/ui/DropdownMenuLabel.vue +0 -20
  76. package/dist/components/ui/DropdownMenuSeparator.vue +0 -16
  77. package/dist/components/ui/DropdownMenuTrigger.vue +0 -38
  78. package/dist/components/ui/FileInput.vue +0 -153
  79. package/dist/components/ui/FormError.vue +0 -20
  80. package/dist/components/ui/FormField.vue +0 -12
  81. package/dist/components/ui/FormInput.vue +0 -46
  82. package/dist/components/ui/FormLabel.vue +0 -19
  83. package/dist/components/ui/FormTextarea.vue +0 -39
  84. package/dist/components/ui/Input.vue +0 -72
  85. package/dist/components/ui/Popover.vue +0 -35
  86. package/dist/components/ui/PopoverContent.vue +0 -66
  87. package/dist/components/ui/PopoverTrigger.vue +0 -36
  88. package/dist/components/ui/RadioGroup.vue +0 -47
  89. package/dist/components/ui/RadioItem.vue +0 -62
  90. package/dist/components/ui/Select.vue +0 -62
  91. package/dist/components/ui/SelectContent.vue +0 -55
  92. package/dist/components/ui/SelectItem.vue +0 -55
  93. package/dist/components/ui/SelectTrigger.vue +0 -70
  94. package/dist/components/ui/SelectValue.vue +0 -27
  95. package/dist/components/ui/Sheet.vue +0 -148
  96. package/dist/components/ui/SheetDescription.vue +0 -22
  97. package/dist/components/ui/SheetFooter.vue +0 -22
  98. package/dist/components/ui/SheetHeader.vue +0 -22
  99. package/dist/components/ui/SheetTitle.vue +0 -22
  100. package/dist/components/ui/Switch.vue +0 -63
  101. package/dist/components/ui/Textarea.vue +0 -73
  102. package/dist/components/ui/Toast.vue +0 -116
  103. package/dist/components/ui/Toaster.vue +0 -76
  104. package/dist/components/ui/Tooltip.vue +0 -42
  105. package/dist/components/ui/TooltipContent.vue +0 -71
  106. package/dist/components/ui/TooltipTrigger.vue +0 -39
  107. package/dist/components/ui/UIProvider.vue +0 -23
  108. package/dist/components/ui/index.d.ts +0 -52
  109. package/dist/components/ui/index.mjs +0 -52
@@ -23,7 +23,7 @@ export interface StyleAdapter {
23
23
  /**
24
24
  * Adapter レジストリ
25
25
  */
26
- export declare const ADAPTER_REGISTRY: any;
26
+ export declare const ADAPTER_REGISTRY: Map<string, StyleAdapter>;
27
27
  /**
28
28
  * Adapter を登録
29
29
  */
@@ -0,0 +1,36 @@
1
+ <template>
2
+ <button
3
+ :disabled="composable.state.value.disabled || composable.state.value.loading"
4
+ :type="composable.state.value.type"
5
+ v-bind="composable.aria_attributes.value"
6
+ @click="handleClick"
7
+ >
8
+ <slot />
9
+ </button>
10
+ </template>
11
+
12
+ <script setup lang="ts">
13
+ import { toRef } from "vue"
14
+ import { useButton } from "../../composables/useButton"
15
+ import type { ButtonProps } from "../../types/button"
16
+
17
+ const props = withDefaults(defineProps<ButtonProps>(), {
18
+ variant: "primary",
19
+ size: "md",
20
+ disabled: false,
21
+ loading: false,
22
+ type: "button"
23
+ })
24
+
25
+ const emit = defineEmits<{
26
+ click: [event: MouseEvent]
27
+ }>()
28
+
29
+ const composable = useButton(toRef(() => props))
30
+
31
+ const handleClick = (event: MouseEvent) => {
32
+ if (!composable.state.value.disabled && !composable.state.value.loading) {
33
+ emit("click", event)
34
+ }
35
+ }
36
+ </script>
@@ -0,0 +1,23 @@
1
+ <template>
2
+ <div>
3
+ <slot />
4
+ </div>
5
+ </template>
6
+
7
+ <script setup lang="ts">
8
+ import { toRef } from "vue"
9
+ import { useCard } from "../../composables/useCard"
10
+ import type { CardProps } from "../../types/card"
11
+
12
+ const props = withDefaults(defineProps<CardProps>(), {
13
+ padding: "md"
14
+ })
15
+
16
+ // Composable is available for state access if needed
17
+ const composable = useCard(toRef(() => props))
18
+
19
+ // Export state for external access
20
+ defineExpose({
21
+ state: composable.state
22
+ })
23
+ </script>
@@ -0,0 +1,44 @@
1
+ <template>
2
+ <button
3
+ type="button"
4
+ :disabled="composable.state.value.disabled"
5
+ :data-state="dataState"
6
+ v-bind="composable.aria_attributes.value"
7
+ @click="handleClick"
8
+ @keydown.space.prevent="handleClick"
9
+ >
10
+ <slot :checked="model" :indeterminate="composable.state.value.indeterminate" />
11
+ </button>
12
+ </template>
13
+
14
+ <script setup lang="ts">
15
+ import { toRef, computed } from "vue"
16
+ import { useCheckbox } from "../../composables/useCheckbox"
17
+ import type { CheckboxProps } from "../../types/checkbox"
18
+
19
+ const props = withDefaults(defineProps<CheckboxProps>(), {
20
+ size: "md",
21
+ disabled: false,
22
+ indeterminate: false
23
+ })
24
+
25
+ const model = defineModel<boolean>({ default: false })
26
+
27
+ const emit = defineEmits<{
28
+ change: [checked: boolean]
29
+ }>()
30
+
31
+ const composable = useCheckbox(toRef(() => props), model)
32
+
33
+ const dataState = computed(() => {
34
+ if (composable.state.value.indeterminate) return "indeterminate"
35
+ return model.value ? "checked" : "unchecked"
36
+ })
37
+
38
+ const handleClick = () => {
39
+ if (!composable.state.value.disabled) {
40
+ model.value = !model.value
41
+ emit("change", model.value)
42
+ }
43
+ }
44
+ </script>
@@ -0,0 +1,99 @@
1
+ <template>
2
+ <Teleport to="body">
3
+ <div
4
+ v-if="is_open"
5
+ role="dialog"
6
+ aria-modal="true"
7
+ :aria-labelledby="titleId"
8
+ :aria-describedby="descriptionId"
9
+ >
10
+ <!-- Overlay -->
11
+ <div
12
+ @click="handleOverlayClick"
13
+ @keydown.escape="close"
14
+ />
15
+ <!-- Content -->
16
+ <div
17
+ ref="content_ref"
18
+ tabindex="-1"
19
+ @keydown.escape="close"
20
+ >
21
+ <slot :close="close" />
22
+ </div>
23
+ </div>
24
+ </Teleport>
25
+ </template>
26
+
27
+ <script setup lang="ts">
28
+ import { ref, watch, onMounted, onUnmounted, provide } from "vue"
29
+ import { useDialog } from "../../composables/useDialog"
30
+ import { DIALOG_KEY } from "../../types/dialog"
31
+ import type { DialogContext } from "../../types/dialog"
32
+
33
+ interface HeadlessDialogProps {
34
+ closeOnOverlay?: boolean
35
+ closeOnEscape?: boolean
36
+ titleId?: string
37
+ descriptionId?: string
38
+ }
39
+
40
+ const props = withDefaults(defineProps<HeadlessDialogProps>(), {
41
+ closeOnOverlay: true,
42
+ closeOnEscape: true
43
+ })
44
+
45
+ const model = defineModel<boolean>("open", { default: false })
46
+
47
+ const content_ref = ref<HTMLElement | null>(null)
48
+ const dialog = useDialog()
49
+ const { is_open, open, close, toggle } = dialog
50
+
51
+ // Sync with v-model
52
+ watch(model, (value) => {
53
+ if (value) {
54
+ open()
55
+ } else {
56
+ close()
57
+ }
58
+ }, { immediate: true })
59
+
60
+ watch(is_open, (value) => {
61
+ model.value = value
62
+ })
63
+
64
+ const handleOverlayClick = () => {
65
+ if (props.closeOnOverlay) {
66
+ close()
67
+ }
68
+ }
69
+
70
+ const handleKeyDown = (event: KeyboardEvent) => {
71
+ if (event.key === "Escape" && props.closeOnEscape && is_open.value) {
72
+ close()
73
+ }
74
+ }
75
+
76
+ // Provide context for child components
77
+ const context: DialogContext = {
78
+ is_open,
79
+ close,
80
+ titleId: props.titleId,
81
+ descriptionId: props.descriptionId
82
+ }
83
+ provide(DIALOG_KEY, context)
84
+
85
+ onMounted(() => {
86
+ document.addEventListener("keydown", handleKeyDown)
87
+ })
88
+
89
+ onUnmounted(() => {
90
+ document.removeEventListener("keydown", handleKeyDown)
91
+ })
92
+
93
+ defineExpose({
94
+ is_open,
95
+ open,
96
+ close,
97
+ toggle
98
+ })
99
+ </script>
@@ -0,0 +1,42 @@
1
+ <template>
2
+ <input
3
+ :type="composable.state.value.type"
4
+ :value="modelValue"
5
+ :disabled="composable.state.value.disabled"
6
+ :readonly="composable.state.value.readonly"
7
+ :placeholder="placeholder"
8
+ :id="id"
9
+ v-bind="composable.aria_attributes.value"
10
+ @input="handleInput"
11
+ @focus="composable.handleFocus"
12
+ @blur="composable.handleBlur"
13
+ />
14
+ </template>
15
+
16
+ <script setup lang="ts">
17
+ import { toRef } from "vue"
18
+ import { useInput } from "../../composables/useInput"
19
+ import type { InputProps } from "../../types/input"
20
+
21
+ interface HeadlessInputProps extends InputProps {
22
+ modelValue?: string
23
+ }
24
+
25
+ const props = withDefaults(defineProps<HeadlessInputProps>(), {
26
+ type: "text",
27
+ size: "md",
28
+ disabled: false,
29
+ readonly: false
30
+ })
31
+
32
+ const emit = defineEmits<{
33
+ "update:modelValue": [value: string]
34
+ }>()
35
+
36
+ const composable = useInput(toRef(() => props))
37
+
38
+ const handleInput = (event: Event) => {
39
+ const target = event.target as HTMLInputElement
40
+ emit("update:modelValue", target.value)
41
+ }
42
+ </script>
@@ -0,0 +1,35 @@
1
+ <template>
2
+ <div
3
+ role="radiogroup"
4
+ :aria-orientation="orientation"
5
+ >
6
+ <slot />
7
+ </div>
8
+ </template>
9
+
10
+ <script setup lang="ts">
11
+ import { provide, computed } from "vue"
12
+ import type { RadioGroupProps, RadioGroupContext } from "../../types/radio"
13
+ import { RADIO_GROUP_KEY } from "../../types/radio"
14
+
15
+ const props = withDefaults(defineProps<RadioGroupProps>(), {
16
+ disabled: false,
17
+ orientation: "vertical"
18
+ })
19
+
20
+ const model = defineModel<string>({ default: "" })
21
+
22
+ const updateValue = (value: string) => {
23
+ if (!props.disabled) {
24
+ model.value = value
25
+ }
26
+ }
27
+
28
+ const context: RadioGroupContext = {
29
+ model_value: model,
30
+ disabled: computed(() => props.disabled),
31
+ updateValue
32
+ }
33
+
34
+ provide(RADIO_GROUP_KEY, context)
35
+ </script>
@@ -0,0 +1,55 @@
1
+ <template>
2
+ <button
3
+ type="button"
4
+ :disabled="composable.state.value.disabled"
5
+ :data-state="composable.state.value.checked ? 'checked' : 'unchecked'"
6
+ v-bind="composable.aria_attributes.value"
7
+ @click="handleClick"
8
+ @keydown="handleKeyDown"
9
+ >
10
+ <slot :checked="composable.state.value.checked" :disabled="composable.state.value.disabled" />
11
+ </button>
12
+ </template>
13
+
14
+ <script setup lang="ts">
15
+ import { inject, toRef } from "vue"
16
+ import { useRadioItem } from "../../composables/useRadio"
17
+ import type { RadioItemProps, RadioGroupContext } from "../../types/radio"
18
+ import { RADIO_GROUP_KEY } from "../../types/radio"
19
+ import { COMPONENT_ERRORS } from "../../constants/errors"
20
+
21
+ const props = withDefaults(defineProps<RadioItemProps>(), {
22
+ size: "md",
23
+ disabled: false
24
+ })
25
+
26
+ const context = inject<RadioGroupContext | null>(RADIO_GROUP_KEY, null)
27
+
28
+ if (!context) {
29
+ throw new Error(COMPONENT_ERRORS.PROVIDER_NOT_FOUND)
30
+ }
31
+
32
+ const composable = useRadioItem(toRef(() => props))
33
+
34
+ const handleClick = () => {
35
+ if (!composable.state.value.disabled) {
36
+ context.updateValue(props.value)
37
+ }
38
+ }
39
+
40
+ const handleKeyDown = (event: KeyboardEvent) => {
41
+ if (composable.state.value.disabled) return
42
+
43
+ switch (event.key) {
44
+ case "Enter":
45
+ case " ":
46
+ event.preventDefault()
47
+ context.updateValue(props.value)
48
+ break
49
+ }
50
+ }
51
+
52
+ defineExpose({
53
+ state: composable.state
54
+ })
55
+ </script>
@@ -0,0 +1,95 @@
1
+ <template>
2
+ <div ref="root_ref">
3
+ <slot
4
+ :is_open="context.is_open.value"
5
+ :selected_value="model"
6
+ :open="context.open"
7
+ :close="context.close"
8
+ :toggle="context.toggle"
9
+ />
10
+ </div>
11
+ </template>
12
+
13
+ <script setup lang="ts">
14
+ import { ref, provide, watch, onMounted, onUnmounted, computed } from "vue"
15
+ import type { SelectProps, SelectContext } from "../../types/select"
16
+ import { SELECT_KEY } from "../../types/select"
17
+
18
+ const props = withDefaults(defineProps<SelectProps>(), {
19
+ size: "md",
20
+ disabled: false,
21
+ placeholder: ""
22
+ })
23
+
24
+ const model = defineModel<string>({ default: "" })
25
+
26
+ const root_ref = ref<HTMLElement | null>(null)
27
+ const trigger_ref = ref<HTMLElement | null>(null)
28
+ const is_open = ref(false)
29
+
30
+ const open = () => {
31
+ if (!props.disabled) {
32
+ is_open.value = true
33
+ }
34
+ }
35
+
36
+ const close = () => {
37
+ is_open.value = false
38
+ }
39
+
40
+ const toggle = () => {
41
+ if (is_open.value) {
42
+ close()
43
+ } else {
44
+ open()
45
+ }
46
+ }
47
+
48
+ const updateValue = (value: string) => {
49
+ model.value = value
50
+ close()
51
+ }
52
+
53
+ const setTriggerRef = (element: HTMLElement | null) => {
54
+ trigger_ref.value = element
55
+ }
56
+
57
+ const handleClickOutside = (event: MouseEvent) => {
58
+ if (!is_open.value) return
59
+ const target = event.target as Node
60
+ if (root_ref.value && !root_ref.value.contains(target)) {
61
+ close()
62
+ }
63
+ }
64
+
65
+ const context: SelectContext = {
66
+ model_value: model,
67
+ is_open,
68
+ disabled: computed(() => props.disabled),
69
+ size: computed(() => props.size ?? "md"),
70
+ placeholder: computed(() => props.placeholder ?? ""),
71
+ trigger_ref,
72
+ updateValue,
73
+ open,
74
+ close,
75
+ toggle,
76
+ setTriggerRef
77
+ }
78
+
79
+ provide(SELECT_KEY, context)
80
+
81
+ onMounted(() => {
82
+ document.addEventListener("mousedown", handleClickOutside)
83
+ })
84
+
85
+ onUnmounted(() => {
86
+ document.removeEventListener("mousedown", handleClickOutside)
87
+ })
88
+
89
+ defineExpose({
90
+ is_open,
91
+ open,
92
+ close,
93
+ toggle
94
+ })
95
+ </script>
@@ -0,0 +1,40 @@
1
+ <template>
2
+ <div
3
+ v-if="composable.state.value.is_open"
4
+ role="listbox"
5
+ tabindex="-1"
6
+ @keydown="handleKeyDown"
7
+ >
8
+ <slot />
9
+ </div>
10
+ </template>
11
+
12
+ <script setup lang="ts">
13
+ import { inject } from "vue"
14
+ import { useSelectContent } from "../../composables/useSelect"
15
+ import type { SelectContext } from "../../types/select"
16
+ import { SELECT_KEY } from "../../types/select"
17
+ import { COMPONENT_ERRORS } from "../../constants/errors"
18
+
19
+ const context = inject<SelectContext | null>(SELECT_KEY, null)
20
+
21
+ if (!context) {
22
+ throw new Error(COMPONENT_ERRORS.PROVIDER_NOT_FOUND)
23
+ }
24
+
25
+ const composable = useSelectContent()
26
+
27
+ const handleKeyDown = (event: KeyboardEvent) => {
28
+ switch (event.key) {
29
+ case "Escape":
30
+ event.preventDefault()
31
+ context.close()
32
+ context.trigger_ref.value?.focus()
33
+ break
34
+ }
35
+ }
36
+
37
+ defineExpose({
38
+ state: composable.state
39
+ })
40
+ </script>
@@ -0,0 +1,44 @@
1
+ <template>
2
+ <div
3
+ role="option"
4
+ :aria-selected="composable.state.value.selected"
5
+ :aria-disabled="composable.state.value.disabled"
6
+ :data-state="composable.state.value.selected ? 'selected' : undefined"
7
+ :data-disabled="composable.state.value.disabled ? '' : undefined"
8
+ @click="handleClick"
9
+ @keydown.enter="handleClick"
10
+ @keydown.space.prevent="handleClick"
11
+ >
12
+ <slot :selected="composable.state.value.selected" :disabled="composable.state.value.disabled" />
13
+ </div>
14
+ </template>
15
+
16
+ <script setup lang="ts">
17
+ import { inject, toRef } from "vue"
18
+ import { useSelectItem } from "../../composables/useSelect"
19
+ import type { SelectContext, SelectItemProps } from "../../types/select"
20
+ import { SELECT_KEY } from "../../types/select"
21
+ import { COMPONENT_ERRORS } from "../../constants/errors"
22
+
23
+ const props = withDefaults(defineProps<SelectItemProps>(), {
24
+ disabled: false
25
+ })
26
+
27
+ const context = inject<SelectContext | null>(SELECT_KEY, null)
28
+
29
+ if (!context) {
30
+ throw new Error(COMPONENT_ERRORS.PROVIDER_NOT_FOUND)
31
+ }
32
+
33
+ const composable = useSelectItem(toRef(() => ({ value: props.value, disabled: props.disabled })))
34
+
35
+ const handleClick = () => {
36
+ if (!composable.state.value.disabled) {
37
+ context.updateValue(props.value)
38
+ }
39
+ }
40
+
41
+ defineExpose({
42
+ state: composable.state
43
+ })
44
+ </script>
@@ -0,0 +1,61 @@
1
+ <template>
2
+ <button
3
+ ref="trigger_element"
4
+ type="button"
5
+ :disabled="composable.state.value.disabled"
6
+ :aria-expanded="composable.state.value.is_open"
7
+ aria-haspopup="listbox"
8
+ @click="handleClick"
9
+ @keydown="handleKeyDown"
10
+ >
11
+ <slot :is_open="composable.state.value.is_open" :disabled="composable.state.value.disabled" />
12
+ </button>
13
+ </template>
14
+
15
+ <script setup lang="ts">
16
+ import { ref, inject, onMounted } from "vue"
17
+ import { useSelectTrigger } from "../../composables/useSelect"
18
+ import type { SelectContext } from "../../types/select"
19
+ import { SELECT_KEY } from "../../types/select"
20
+ import { COMPONENT_ERRORS } from "../../constants/errors"
21
+
22
+ const trigger_element = ref<HTMLElement | null>(null)
23
+ const context = inject<SelectContext | null>(SELECT_KEY, null)
24
+
25
+ if (!context) {
26
+ throw new Error(COMPONENT_ERRORS.PROVIDER_NOT_FOUND)
27
+ }
28
+
29
+ const composable = useSelectTrigger()
30
+
31
+ const handleClick = () => {
32
+ if (!composable.state.value.disabled) {
33
+ context.toggle()
34
+ }
35
+ }
36
+
37
+ const handleKeyDown = (event: KeyboardEvent) => {
38
+ if (composable.state.value.disabled) return
39
+
40
+ switch (event.key) {
41
+ case "Enter":
42
+ case " ":
43
+ case "ArrowDown":
44
+ event.preventDefault()
45
+ context.open()
46
+ break
47
+ case "ArrowUp":
48
+ event.preventDefault()
49
+ context.open()
50
+ break
51
+ }
52
+ }
53
+
54
+ onMounted(() => {
55
+ context.setTriggerRef(trigger_element.value)
56
+ })
57
+
58
+ defineExpose({
59
+ state: composable.state
60
+ })
61
+ </script>
@@ -0,0 +1,43 @@
1
+ <template>
2
+ <button
3
+ type="button"
4
+ :disabled="composable.state.value.disabled"
5
+ :data-state="model ? 'checked' : 'unchecked'"
6
+ v-bind="composable.aria_attributes.value"
7
+ @click="handleClick"
8
+ >
9
+ <slot :checked="model">
10
+ <span :data-state="model ? 'checked' : 'unchecked'" />
11
+ </slot>
12
+ </button>
13
+ </template>
14
+
15
+ <script setup lang="ts">
16
+ import { toRef } from "vue"
17
+ import { useSwitch } from "../../composables/useSwitch"
18
+ import type { SwitchProps } from "../../types/switch"
19
+
20
+ const props = withDefaults(defineProps<SwitchProps>(), {
21
+ size: "md",
22
+ disabled: false
23
+ })
24
+
25
+ const model = defineModel<boolean>({ default: false })
26
+
27
+ const emit = defineEmits<{
28
+ change: [checked: boolean]
29
+ }>()
30
+
31
+ const composable = useSwitch(toRef(() => props), model)
32
+
33
+ const handleClick = () => {
34
+ if (!composable.state.value.disabled) {
35
+ model.value = !model.value
36
+ emit("change", model.value)
37
+ }
38
+ }
39
+
40
+ defineExpose({
41
+ state: composable.state
42
+ })
43
+ </script>
@@ -0,0 +1,49 @@
1
+ <template>
2
+ <textarea
3
+ :value="modelValue"
4
+ :disabled="composable.state.value.disabled"
5
+ :readonly="composable.state.value.readonly"
6
+ :placeholder="placeholder"
7
+ :rows="rows"
8
+ :id="id"
9
+ v-bind="composable.aria_attributes.value"
10
+ @input="handleInput"
11
+ @focus="composable.handleFocus"
12
+ @blur="composable.handleBlur"
13
+ />
14
+ </template>
15
+
16
+ <script setup lang="ts">
17
+ import { toRef } from "vue"
18
+ import { useTextarea } from "../../composables/useTextarea"
19
+ import type { TextareaProps } from "../../types/textarea"
20
+
21
+ interface HeadlessTextareaProps extends TextareaProps {
22
+ modelValue?: string
23
+ id?: string
24
+ }
25
+
26
+ const props = withDefaults(defineProps<HeadlessTextareaProps>(), {
27
+ size: "md",
28
+ disabled: false,
29
+ readonly: false,
30
+ rows: 3,
31
+ resize: "vertical"
32
+ })
33
+
34
+ const emit = defineEmits<{
35
+ "update:modelValue": [value: string]
36
+ }>()
37
+
38
+ const composable = useTextarea(toRef(() => props))
39
+
40
+ const handleInput = (event: Event) => {
41
+ const target = event.target as HTMLTextAreaElement
42
+ emit("update:modelValue", target.value)
43
+ }
44
+
45
+ defineExpose({
46
+ state: composable.state,
47
+ is_focused: composable.is_focused
48
+ })
49
+ </script>