ropav 0.0.2 → 0.0.4

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 (74) hide show
  1. package/README.md +32 -3
  2. package/dist/base.css +153 -77
  3. package/dist/button.css +276 -32
  4. package/dist/button.js +40 -19
  5. package/dist/button.js.map +1 -1
  6. package/dist/checkbox.css +127 -19
  7. package/dist/checkbox.js +57 -44
  8. package/dist/checkbox.js.map +1 -1
  9. package/dist/components/button/button.d.ts +2 -2
  10. package/dist/components/button/index.d.ts +1 -1
  11. package/dist/components/button/types.d.ts +8 -1
  12. package/dist/components/checkbox/index.d.ts +1 -1
  13. package/dist/components/checkbox/types.d.ts +8 -0
  14. package/dist/components/input/index.d.ts +1 -1
  15. package/dist/components/input/input.d.ts +5 -1
  16. package/dist/components/input/types.d.ts +5 -0
  17. package/dist/components/radio/index.d.ts +1 -1
  18. package/dist/components/radio/types.d.ts +12 -0
  19. package/dist/components/select/types.d.ts +3 -0
  20. package/dist/components/select/useSelect.d.ts +2 -0
  21. package/dist/components/switch/index.d.ts +1 -1
  22. package/dist/components/switch/types.d.ts +4 -0
  23. package/dist/components/textarea/index.d.ts +1 -1
  24. package/dist/components/textarea/types.d.ts +5 -0
  25. package/dist/components/textarea/useTextarea.d.ts +1 -0
  26. package/dist/index.d.ts +0 -2
  27. package/dist/index.js +2 -4
  28. package/dist/input.css +91 -15
  29. package/dist/input.js +66 -23
  30. package/dist/input.js.map +1 -1
  31. package/dist/radio.css +84 -16
  32. package/dist/radio.js +35 -8
  33. package/dist/radio.js.map +1 -1
  34. package/dist/select.css +97 -32
  35. package/dist/select.js +128 -88
  36. package/dist/select.js.map +1 -1
  37. package/dist/switch.css +75 -20
  38. package/dist/switch.js +8 -4
  39. package/dist/switch.js.map +1 -1
  40. package/dist/textarea.css +84 -14
  41. package/dist/textarea.js +33 -5
  42. package/dist/textarea.js.map +1 -1
  43. package/dist/useControlState.js +4 -0
  44. package/dist/useControlState.js.map +1 -1
  45. package/package.json +11 -10
  46. package/src/styles/_mixins.scss +21 -11
  47. package/src/styles/_tokens.scss +168 -96
  48. package/src/styles/_variables.scss +30 -28
  49. package/dist/components/_internal/icons/CheckIcon.d.ts +0 -18
  50. package/dist/components/_internal/icons/ChevronDownIcon.d.ts +0 -18
  51. package/dist/components/_internal/icons/MinusIcon.d.ts +0 -18
  52. package/dist/components/_internal/icons/index.d.ts +0 -3
  53. package/dist/components/_internal/optionList.d.ts +0 -9
  54. package/dist/components/tabs/index.d.ts +0 -5
  55. package/dist/components/tabs/index.js +0 -2
  56. package/dist/components/tabs/tab-panel.d.ts +0 -21
  57. package/dist/components/tabs/tabs-list.d.ts +0 -20
  58. package/dist/components/tabs/tabs-trigger.d.ts +0 -21
  59. package/dist/components/tabs/tabs.d.ts +0 -23
  60. package/dist/components/tabs/types.d.ts +0 -18
  61. package/dist/components/tabs/useTabs.d.ts +0 -19
  62. package/dist/components/tooltip/index.d.ts +0 -2
  63. package/dist/components/tooltip/index.js +0 -2
  64. package/dist/components/tooltip/tooltip.d.ts +0 -23
  65. package/dist/components/tooltip/types.d.ts +0 -5
  66. package/dist/components/tooltip/useTooltip.d.ts +0 -8
  67. package/dist/tabs.css +0 -45
  68. package/dist/tabs.js +0 -205
  69. package/dist/tabs.js.map +0 -1
  70. package/dist/tooltip.css +0 -78
  71. package/dist/tooltip.js +0 -112
  72. package/dist/tooltip.js.map +0 -1
  73. package/dist/useRequiredInject.js +0 -11
  74. package/dist/useRequiredInject.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"select.js","names":[],"sources":["../src/components/_internal/icons/ChevronDownIcon.vue","../src/components/_internal/icons/ChevronDownIcon.vue","../src/components/_internal/optionList.ts","../src/composables/useClickOutside.ts","../src/composables/useListNavigation.ts","../src/components/select/useSelect.ts","../src/components/select/select.vue","../src/components/select/select.vue"],"sourcesContent":["<template>\n <svg viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\">\n <path d=\"M4 6l4 4 4-4\" />\n </svg>\n</template>\n\n<script lang=\"ts\" setup vapor>\ndefineOptions({ name: 'ChevronDownIcon' });\n</script>\n","<template>\n <svg viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\">\n <path d=\"M4 6l4 4 4-4\" />\n </svg>\n</template>\n\n<script lang=\"ts\" setup vapor>\ndefineOptions({ name: 'ChevronDownIcon' });\n</script>\n","export interface OptionListItem {\n label: string;\n value: string | number;\n disabled?: boolean;\n}\n\nexport type OptionListValue = string | number | null;\n\nexport function hasOptionListValue(value: OptionListValue) {\n return value != null && value !== '';\n}\n\nexport function getOptionListDisplayLabel<T extends OptionListItem>(\n options: T[] | undefined,\n value: OptionListValue,\n) {\n if (!hasOptionListValue(value)) return '';\n return options?.find((option) => option.value === value)?.label ?? '';\n}\n\nexport function getOptionListActiveDescendantId(baseId: string, focusedIndex: number) {\n return focusedIndex < 0 ? undefined : `${baseId}-option-${focusedIndex}`;\n}\n","import { watch, onMounted, onBeforeUnmount, type Ref } from 'vue';\n\nexport function useClickOutside(\n target: Ref<HTMLElement | null>,\n active: Ref<boolean>,\n callback: () => void,\n) {\n let isListening = false;\n let stopWatch: (() => void) | undefined;\n\n function handler(e: MouseEvent) {\n if (target.value && !target.value.contains(e.target as Node)) {\n callback();\n }\n }\n\n function setListening(value: boolean) {\n if (typeof document === 'undefined' || value === isListening) return;\n isListening = value;\n\n if (value) {\n document.addEventListener('click', handler, true);\n } else {\n document.removeEventListener('click', handler, true);\n }\n }\n\n onMounted(() => {\n stopWatch = watch(active, setListening, { immediate: true });\n });\n\n onBeforeUnmount(() => {\n stopWatch?.();\n setListening(false);\n });\n}\n","import { computed, ref } from 'vue';\n\nexport interface ListNavigationItem {\n disabled?: boolean;\n}\n\nexport interface UseListNavigationOptions<T extends ListNavigationItem> {\n items: () => T[];\n isSelected?: (item: T) => boolean;\n loop?: boolean;\n}\n\nexport function useListNavigation<T extends ListNavigationItem>(\n options: UseListNavigationOptions<T>,\n) {\n const localFocusedIndex = ref(-1);\n const loop = options.loop ?? true;\n\n const enabledIndexes = computed(() =>\n options.items()\n .map((item, index) => item.disabled ? -1 : index)\n .filter((index) => index >= 0),\n );\n\n function resetFocus() {\n localFocusedIndex.value = -1;\n }\n\n function setNearest(index: number, direction: 1 | -1) {\n const indexes = enabledIndexes.value;\n if (indexes.length === 0) {\n resetFocus();\n return;\n }\n\n if (options.items()[index] && !options.items()[index]?.disabled) {\n localFocusedIndex.value = index;\n return;\n }\n\n const next = direction === 1\n ? indexes.find((i) => i > index)\n : [...indexes].reverse().find((i) => i < index);\n\n if (next !== undefined) {\n localFocusedIndex.value = next;\n } else if (loop) {\n localFocusedIndex.value = direction === 1 ? indexes[0]! : indexes[indexes.length - 1]!;\n }\n }\n\n function focusSelected() {\n const items = options.items();\n const selectedIndex = options.isSelected\n ? items.findIndex((item) => options.isSelected!(item) && !item.disabled)\n : -1;\n if (selectedIndex >= 0) {\n localFocusedIndex.value = selectedIndex;\n } else {\n focusFirst();\n }\n }\n\n function focusFirst() {\n const first = enabledIndexes.value[0];\n localFocusedIndex.value = first ?? -1;\n }\n\n function focusLast() {\n const indexes = enabledIndexes.value;\n localFocusedIndex.value = indexes[indexes.length - 1] ?? -1;\n }\n\n function moveFocus(delta: 1 | -1) {\n if (localFocusedIndex.value < 0) {\n delta === 1 ? focusFirst() : focusLast();\n return;\n }\n setNearest(localFocusedIndex.value + delta, delta);\n }\n\n return {\n focusedIndex: localFocusedIndex,\n resetFocus,\n focusSelected,\n focusFirst,\n focusLast,\n moveFocus,\n };\n}\n","import { computed, nextTick, ref, useId } from 'vue';\nimport {\n getOptionListActiveDescendantId,\n getOptionListDisplayLabel,\n hasOptionListValue,\n} from '@/components/_internal/optionList';\nimport { useClickOutside } from '@/composables/useClickOutside';\nimport { useControlState } from '@/composables/useControlState';\nimport { useListNavigation } from '@/composables/useListNavigation';\nimport { bem } from '@/utils/bem';\nimport type { SelectOption, SelectProps } from './types';\n\ntype SelectValue = string | number | null;\n\nexport function useSelect(\n props: Readonly<SelectProps>,\n emitUpdate: (value: SelectValue) => void,\n) {\n const selectRef = ref<HTMLElement | null>(null);\n const triggerRef = ref<HTMLElement | null>(null);\n const isOpen = ref(false);\n\n const selectId = useId();\n const listboxId = `${selectId}-listbox`;\n\n const control = useControlState(props);\n const visibleOptions = computed(() => props.options ?? []);\n\n const navigation = useListNavigation<SelectOption>({\n items: () => visibleOptions.value,\n isSelected: (item) => item.value === props.modelValue,\n });\n\n const focusedIndex = navigation.focusedIndex;\n const activeDescendantId = computed(() =>\n getOptionListActiveDescendantId(selectId, focusedIndex.value),\n );\n\n const rootClass = computed(() =>\n bem('rp-select', {\n open: isOpen.value,\n disabled: control.disabled,\n invalid: control.invalid,\n }),\n );\n\n const hasValue = computed(() => hasOptionListValue(props.modelValue));\n const displayLabel = computed(() => getOptionListDisplayLabel(props.options, props.modelValue));\n\n function focusTrigger() {\n nextTick(() => triggerRef.value?.focus());\n }\n\n function open() {\n if (control.disabled || isOpen.value) return;\n isOpen.value = true;\n navigation.focusSelected();\n }\n\n function close() {\n isOpen.value = false;\n navigation.resetFocus();\n }\n\n function toggle() {\n if (control.disabled) return;\n if (isOpen.value) {\n close();\n focusTrigger();\n } else {\n open();\n }\n }\n\n function selectOption(option: SelectOption) {\n if (option.disabled) return;\n emitUpdate(option.value);\n close();\n focusTrigger();\n }\n\n function onOptionMouseenter(option: SelectOption, index: number) {\n if (!option.disabled) focusedIndex.value = index;\n }\n\n function selectFocusedOption() {\n if (focusedIndex.value < 0 || focusedIndex.value >= visibleOptions.value.length) return;\n selectOption(visibleOptions.value[focusedIndex.value]!);\n }\n\n function onTriggerKeydown(e: KeyboardEvent) {\n if (control.disabled) return;\n\n switch (e.key) {\n case 'Enter':\n e.preventDefault();\n if (!isOpen.value) open();\n else selectFocusedOption();\n break;\n case ' ':\n e.preventDefault();\n if (!isOpen.value) open();\n else selectFocusedOption();\n break;\n case 'ArrowDown':\n e.preventDefault();\n if (!isOpen.value) open();\n else navigation.moveFocus(1);\n break;\n case 'ArrowUp':\n e.preventDefault();\n if (!isOpen.value) {\n open();\n navigation.focusLast();\n } else {\n navigation.moveFocus(-1);\n }\n break;\n case 'Home':\n if (isOpen.value) {\n e.preventDefault();\n navigation.focusFirst();\n }\n break;\n case 'End':\n if (isOpen.value) {\n e.preventDefault();\n navigation.focusLast();\n }\n break;\n case 'Escape':\n e.preventDefault();\n close();\n focusTrigger();\n break;\n }\n }\n\n useClickOutside(selectRef, isOpen, close);\n\n return {\n selectRef,\n triggerRef,\n isOpen,\n selectId,\n listboxId,\n control,\n visibleOptions,\n focusedIndex,\n activeDescendantId,\n rootClass,\n hasValue,\n displayLabel,\n toggle,\n selectOption,\n onOptionMouseenter,\n onTriggerKeydown,\n };\n}\n","<template>\n <div :class=\"rootClass\" ref=\"selectRef\">\n <input v-if=\"name\" type=\"hidden\" :name=\"name\" :value=\"modelValue ?? ''\" :disabled=\"control.disabled || undefined\" />\n <div\n :id=\"control.id\"\n ref=\"triggerRef\"\n class=\"rp-select__trigger\"\n role=\"combobox\"\n :aria-expanded=\"isOpen\"\n aria-haspopup=\"listbox\"\n :aria-activedescendant=\"activeDescendantId\"\n :aria-controls=\"listboxId\"\n :aria-disabled=\"control.disabled || undefined\"\n :aria-invalid=\"control.invalid || undefined\"\n :aria-required=\"control.required || undefined\"\n :aria-label=\"ariaLabel || undefined\"\n :aria-labelledby=\"control.ariaLabelledby\"\n :aria-describedby=\"control.ariaDescribedby\"\n :data-state=\"isOpen ? 'open' : 'closed'\"\n :data-disabled=\"control.disabled || undefined\"\n :tabindex=\"control.disabled ? -1 : 0\"\n @click=\"toggle\"\n @keydown=\"onTriggerKeydown\"\n >\n <span class=\"rp-select__value\" :class=\"{ 'rp-select__placeholder': !hasValue }\">\n {{ displayLabel || placeholder }}\n </span>\n\n <span class=\"rp-select__arrow\" :class=\"{ 'rp-select__arrow--open': isOpen }\">\n <ChevronDownIcon />\n </span>\n </div>\n\n <Transition name=\"rp-select-dropdown\">\n <div v-if=\"isOpen\" class=\"rp-select__dropdown\" role=\"listbox\" :id=\"listboxId\">\n <div\n v-if=\"visibleOptions.length === 0\"\n class=\"rp-select__empty\"\n >\n <slot name=\"empty\">No options</slot>\n </div>\n <div\n v-for=\"(option, index) in visibleOptions\"\n :key=\"option.value\"\n role=\"option\"\n :id=\"`${selectId}-option-${index}`\"\n :aria-selected=\"option.value === modelValue\"\n :aria-disabled=\"option.disabled || undefined\"\n :data-selected=\"option.value === modelValue || undefined\"\n :data-disabled=\"option.disabled || undefined\"\n :class=\"[\n 'rp-select__option',\n {\n 'rp-select__option--selected': option.value === modelValue,\n 'rp-select__option--focused': index === focusedIndex,\n 'rp-select__option--disabled': option.disabled,\n },\n ]\"\n @click=\"selectOption(option)\"\n @mouseenter=\"onOptionMouseenter(option, index)\"\n >\n {{ option.label }}\n </div>\n </div>\n </Transition>\n </div>\n</template>\n\n<script lang=\"ts\" setup vapor>\nimport { ChevronDownIcon } from '@/components/_internal/icons';\nimport { useSelect } from './useSelect';\nimport type { SelectProps } from './types';\n\ndefineOptions({ name: 'RpSelect' });\n\nconst props = withDefaults(defineProps<SelectProps>(), {\n options: () => [],\n placeholder: 'Select...',\n disabled: undefined,\n required: undefined,\n invalid: undefined,\n});\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string | number | null];\n}>();\n\nconst {\n selectRef,\n triggerRef,\n isOpen,\n selectId,\n listboxId,\n control,\n visibleOptions,\n focusedIndex,\n activeDescendantId,\n rootClass,\n hasValue,\n displayLabel,\n toggle,\n selectOption,\n onOptionMouseenter,\n onTriggerKeydown,\n} = useSelect(props, (value) => {\n emit('update:modelValue', value);\n});\n\nvoid selectRef;\nvoid triggerRef;\n</script>\n\n<style src=\"./select.scss\" lang=\"scss\" scoped></style>\n","<template>\n <div :class=\"rootClass\" ref=\"selectRef\">\n <input v-if=\"name\" type=\"hidden\" :name=\"name\" :value=\"modelValue ?? ''\" :disabled=\"control.disabled || undefined\" />\n <div\n :id=\"control.id\"\n ref=\"triggerRef\"\n class=\"rp-select__trigger\"\n role=\"combobox\"\n :aria-expanded=\"isOpen\"\n aria-haspopup=\"listbox\"\n :aria-activedescendant=\"activeDescendantId\"\n :aria-controls=\"listboxId\"\n :aria-disabled=\"control.disabled || undefined\"\n :aria-invalid=\"control.invalid || undefined\"\n :aria-required=\"control.required || undefined\"\n :aria-label=\"ariaLabel || undefined\"\n :aria-labelledby=\"control.ariaLabelledby\"\n :aria-describedby=\"control.ariaDescribedby\"\n :data-state=\"isOpen ? 'open' : 'closed'\"\n :data-disabled=\"control.disabled || undefined\"\n :tabindex=\"control.disabled ? -1 : 0\"\n @click=\"toggle\"\n @keydown=\"onTriggerKeydown\"\n >\n <span class=\"rp-select__value\" :class=\"{ 'rp-select__placeholder': !hasValue }\">\n {{ displayLabel || placeholder }}\n </span>\n\n <span class=\"rp-select__arrow\" :class=\"{ 'rp-select__arrow--open': isOpen }\">\n <ChevronDownIcon />\n </span>\n </div>\n\n <Transition name=\"rp-select-dropdown\">\n <div v-if=\"isOpen\" class=\"rp-select__dropdown\" role=\"listbox\" :id=\"listboxId\">\n <div\n v-if=\"visibleOptions.length === 0\"\n class=\"rp-select__empty\"\n >\n <slot name=\"empty\">No options</slot>\n </div>\n <div\n v-for=\"(option, index) in visibleOptions\"\n :key=\"option.value\"\n role=\"option\"\n :id=\"`${selectId}-option-${index}`\"\n :aria-selected=\"option.value === modelValue\"\n :aria-disabled=\"option.disabled || undefined\"\n :data-selected=\"option.value === modelValue || undefined\"\n :data-disabled=\"option.disabled || undefined\"\n :class=\"[\n 'rp-select__option',\n {\n 'rp-select__option--selected': option.value === modelValue,\n 'rp-select__option--focused': index === focusedIndex,\n 'rp-select__option--disabled': option.disabled,\n },\n ]\"\n @click=\"selectOption(option)\"\n @mouseenter=\"onOptionMouseenter(option, index)\"\n >\n {{ option.label }}\n </div>\n </div>\n </Transition>\n </div>\n</template>\n\n<script lang=\"ts\" setup vapor>\nimport { ChevronDownIcon } from '@/components/_internal/icons';\nimport { useSelect } from './useSelect';\nimport type { SelectProps } from './types';\n\ndefineOptions({ name: 'RpSelect' });\n\nconst props = withDefaults(defineProps<SelectProps>(), {\n options: () => [],\n placeholder: 'Select...',\n disabled: undefined,\n required: undefined,\n invalid: undefined,\n});\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string | number | null];\n}>();\n\nconst {\n selectRef,\n triggerRef,\n isOpen,\n selectId,\n listboxId,\n control,\n visibleOptions,\n focusedIndex,\n activeDescendantId,\n rootClass,\n hasValue,\n displayLabel,\n toggle,\n selectOption,\n onOptionMouseenter,\n onTriggerKeydown,\n} = useSelect(props, (value) => {\n emit('update:modelValue', value);\n});\n\nvoid selectRef;\nvoid triggerRef;\n</script>\n\n<style src=\"./select.scss\" lang=\"scss\" scoped></style>\n"],"mappings":"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"}
1
+ {"version":3,"file":"select.js","names":[],"sources":["../src/composables/useClickOutside.ts","../src/composables/useListNavigation.ts","../src/components/select/useSelect.ts","../src/components/select/select.vue","../src/components/select/select.vue"],"sourcesContent":["import { watch, onMounted, onBeforeUnmount, type Ref } from 'vue';\n\nexport function useClickOutside(\n target: Ref<HTMLElement | null>,\n active: Ref<boolean>,\n callback: () => void,\n) {\n let isListening = false;\n let stopWatch: (() => void) | undefined;\n\n function handler(e: MouseEvent) {\n if (target.value && !target.value.contains(e.target as Node)) {\n callback();\n }\n }\n\n function setListening(value: boolean) {\n if (typeof document === 'undefined' || value === isListening) return;\n isListening = value;\n\n if (value) {\n document.addEventListener('click', handler, true);\n } else {\n document.removeEventListener('click', handler, true);\n }\n }\n\n onMounted(() => {\n stopWatch = watch(active, setListening, { immediate: true });\n });\n\n onBeforeUnmount(() => {\n stopWatch?.();\n setListening(false);\n });\n}\n","import { computed, ref } from 'vue';\n\nexport interface ListNavigationItem {\n disabled?: boolean;\n}\n\nexport interface UseListNavigationOptions<T extends ListNavigationItem> {\n items: () => T[];\n isSelected?: (item: T) => boolean;\n loop?: boolean;\n}\n\nexport function useListNavigation<T extends ListNavigationItem>(\n options: UseListNavigationOptions<T>,\n) {\n const localFocusedIndex = ref(-1);\n const loop = options.loop ?? true;\n\n const enabledIndexes = computed(() =>\n options\n .items()\n .map((item, index) => (item.disabled ? -1 : index))\n .filter((index) => index >= 0),\n );\n\n function resetFocus() {\n localFocusedIndex.value = -1;\n }\n\n function setNearest(index: number, direction: 1 | -1) {\n const indexes = enabledIndexes.value;\n if (indexes.length === 0) {\n resetFocus();\n return;\n }\n\n if (options.items()[index] && !options.items()[index]?.disabled) {\n localFocusedIndex.value = index;\n return;\n }\n\n let next: number | undefined;\n if (direction === 1) {\n next = indexes.find((i) => i > index);\n } else {\n for (let i = indexes.length - 1; i >= 0; i -= 1) {\n const candidate = indexes[i]!;\n if (candidate < index) {\n next = candidate;\n break;\n }\n }\n }\n\n if (next !== undefined) {\n localFocusedIndex.value = next;\n } else if (loop) {\n localFocusedIndex.value = direction === 1 ? indexes[0]! : indexes[indexes.length - 1]!;\n }\n }\n\n function focusSelected() {\n const items = options.items();\n const selectedIndex = options.isSelected\n ? items.findIndex((item) => options.isSelected!(item) && !item.disabled)\n : -1;\n if (selectedIndex >= 0) {\n localFocusedIndex.value = selectedIndex;\n } else {\n focusFirst();\n }\n }\n\n function focusFirst() {\n const first = enabledIndexes.value[0];\n localFocusedIndex.value = first ?? -1;\n }\n\n function focusLast() {\n const indexes = enabledIndexes.value;\n localFocusedIndex.value = indexes[indexes.length - 1] ?? -1;\n }\n\n function moveFocus(delta: 1 | -1) {\n if (localFocusedIndex.value < 0) {\n if (delta === 1) {\n focusFirst();\n } else {\n focusLast();\n }\n return;\n }\n setNearest(localFocusedIndex.value + delta, delta);\n }\n\n return {\n focusedIndex: localFocusedIndex,\n resetFocus,\n focusSelected,\n focusFirst,\n focusLast,\n moveFocus,\n };\n}\n","import { computed, nextTick, ref, useId } from 'vue';\nimport { useClickOutside } from '@/composables/useClickOutside';\nimport { useControlState } from '@/composables/useControlState';\nimport { useListNavigation } from '@/composables/useListNavigation';\nimport { bem } from '@/utils/bem';\nimport type { SelectOption, SelectProps } from './types';\n\ntype SelectValue = string | number | null;\n\nfunction hasSelectValue(value: SelectValue) {\n return value != null && value !== '';\n}\n\nfunction getSelectDisplayLabel(options: SelectOption[] | undefined, value: SelectValue) {\n if (!hasSelectValue(value)) return '';\n return options?.find((option) => option.value === value)?.label ?? '';\n}\n\nfunction getSelectActiveDescendantId(baseId: string, focusedIndex: number) {\n return focusedIndex < 0 ? undefined : `${baseId}-option-${focusedIndex}`;\n}\n\nexport function useSelect(props: Readonly<SelectProps>, emitUpdate: (value: SelectValue) => void) {\n const selectRef = ref<HTMLElement | null>(null);\n const triggerRef = ref<HTMLElement | null>(null);\n const isOpen = ref(false);\n\n const selectId = useId();\n const listboxId = `${selectId}-listbox`;\n\n const control = useControlState(props);\n const visibleOptions = computed(() => props.options ?? []);\n\n const navigation = useListNavigation<SelectOption>({\n items: () => visibleOptions.value,\n isSelected: (item) => item.value === props.modelValue,\n });\n\n const focusedIndex = navigation.focusedIndex;\n const activeDescendantId = computed(() =>\n getSelectActiveDescendantId(selectId, focusedIndex.value),\n );\n\n const rootClass = computed(() =>\n bem('rp-select', {\n open: isOpen.value,\n [`radius-${props.radius}`]: Boolean(props.radius),\n disabled: control.disabled,\n invalid: control.invalid,\n }),\n );\n\n const hasValue = computed(() => hasSelectValue(props.modelValue));\n const displayLabel = computed(() => getSelectDisplayLabel(props.options, props.modelValue));\n const canClear = computed(() =>\n Boolean(props.clearable && hasValue.value && !control.disabled),\n );\n\n function focusTrigger() {\n nextTick(() => triggerRef.value?.focus());\n }\n\n function open() {\n if (control.disabled || isOpen.value) return;\n isOpen.value = true;\n navigation.focusSelected();\n }\n\n function close() {\n isOpen.value = false;\n navigation.resetFocus();\n }\n\n function toggle() {\n if (control.disabled) return;\n if (isOpen.value) {\n close();\n focusTrigger();\n } else {\n open();\n }\n }\n\n function selectOption(option: SelectOption) {\n if (option.disabled) return;\n emitUpdate(option.value);\n close();\n focusTrigger();\n }\n\n function clearSelection() {\n if (!canClear.value) return;\n emitUpdate(null);\n close();\n focusTrigger();\n }\n\n function onOptionMouseenter(option: SelectOption, index: number) {\n if (!option.disabled) focusedIndex.value = index;\n }\n\n function selectFocusedOption() {\n if (focusedIndex.value < 0 || focusedIndex.value >= visibleOptions.value.length) return;\n selectOption(visibleOptions.value[focusedIndex.value]!);\n }\n\n function onTriggerKeydown(e: KeyboardEvent) {\n if (control.disabled) return;\n\n switch (e.key) {\n case 'Enter':\n e.preventDefault();\n if (!isOpen.value) open();\n else selectFocusedOption();\n break;\n case ' ':\n e.preventDefault();\n if (!isOpen.value) open();\n else selectFocusedOption();\n break;\n case 'ArrowDown':\n e.preventDefault();\n if (!isOpen.value) open();\n else navigation.moveFocus(1);\n break;\n case 'ArrowUp':\n e.preventDefault();\n if (!isOpen.value) {\n open();\n navigation.focusLast();\n } else {\n navigation.moveFocus(-1);\n }\n break;\n case 'Home':\n if (isOpen.value) {\n e.preventDefault();\n navigation.focusFirst();\n }\n break;\n case 'End':\n if (isOpen.value) {\n e.preventDefault();\n navigation.focusLast();\n }\n break;\n case 'Escape':\n e.preventDefault();\n close();\n focusTrigger();\n break;\n }\n }\n\n useClickOutside(selectRef, isOpen, close);\n\n return {\n selectRef,\n triggerRef,\n isOpen,\n selectId,\n listboxId,\n control,\n visibleOptions,\n focusedIndex,\n activeDescendantId,\n rootClass,\n hasValue,\n displayLabel,\n canClear,\n toggle,\n selectOption,\n clearSelection,\n onOptionMouseenter,\n onTriggerKeydown,\n };\n}\n","<template>\n <div :class=\"rootClass\" ref=\"selectRef\">\n <input\n v-if=\"name\"\n type=\"hidden\"\n :name=\"name\"\n :value=\"modelValue ?? ''\"\n :disabled=\"control.disabled || undefined\"\n />\n <div\n :id=\"control.id\"\n ref=\"triggerRef\"\n class=\"rp-select__trigger\"\n role=\"combobox\"\n :aria-expanded=\"isOpen\"\n aria-haspopup=\"listbox\"\n :aria-activedescendant=\"activeDescendantId\"\n :aria-controls=\"listboxId\"\n :aria-disabled=\"control.disabled || undefined\"\n :aria-invalid=\"control.invalid || undefined\"\n :aria-required=\"control.required || undefined\"\n :aria-label=\"ariaLabel || undefined\"\n :aria-labelledby=\"control.ariaLabelledby\"\n :aria-describedby=\"control.ariaDescribedby\"\n :data-state=\"isOpen ? 'open' : 'closed'\"\n :data-disabled=\"control.disabled || undefined\"\n :tabindex=\"control.disabled ? -1 : 0\"\n @click=\"toggle\"\n @keydown=\"onTriggerKeydown\"\n >\n <span class=\"rp-select__value\" :class=\"{ 'rp-select__placeholder': !hasValue }\">\n {{ displayLabel || placeholder }}\n </span>\n\n <span class=\"rp-select__indicator\">\n <button\n v-if=\"canClear\"\n type=\"button\"\n class=\"rp-select__clear\"\n aria-label=\"Clear selection\"\n tabindex=\"-1\"\n @mousedown.prevent\n @click.stop=\"clearSelection\"\n >\n <XIcon />\n </button>\n <span class=\"rp-select__arrow\" aria-hidden=\"true\">\n <ChevronsUpDownIcon />\n </span>\n </span>\n </div>\n\n <Transition name=\"rp-select-dropdown\">\n <div v-if=\"isOpen\" class=\"rp-select__dropdown\" role=\"listbox\" :id=\"listboxId\">\n <div v-if=\"visibleOptions.length === 0\" class=\"rp-select__empty\">\n <slot name=\"empty\">No options</slot>\n </div>\n <div\n v-for=\"(option, index) in visibleOptions\"\n :key=\"option.value\"\n role=\"option\"\n :id=\"`${selectId}-option-${index}`\"\n :aria-selected=\"option.value === modelValue\"\n :aria-disabled=\"option.disabled || undefined\"\n :data-selected=\"option.value === modelValue || undefined\"\n :data-disabled=\"option.disabled || undefined\"\n :class=\"[\n 'rp-select__option',\n {\n 'rp-select__option--selected': option.value === modelValue,\n 'rp-select__option--focused': index === focusedIndex,\n 'rp-select__option--disabled': option.disabled,\n },\n ]\"\n @click=\"selectOption(option)\"\n @mouseenter=\"onOptionMouseenter(option, index)\"\n >\n {{ option.label }}\n </div>\n </div>\n </Transition>\n </div>\n</template>\n\n<script lang=\"ts\" setup vapor>\nimport ChevronsUpDownIcon from '~icons/lucide/chevrons-up-down';\nimport XIcon from '~icons/lucide/x';\nimport { useSelect } from './useSelect';\nimport type { SelectProps } from './types';\n\ndefineOptions({ name: 'RpSelect' });\n\nconst props = withDefaults(defineProps<SelectProps>(), {\n options: () => [],\n placeholder: 'Select...',\n clearable: false,\n disabled: undefined,\n required: undefined,\n invalid: undefined,\n});\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string | number | null];\n}>();\n\nconst {\n selectRef,\n triggerRef,\n isOpen,\n selectId,\n listboxId,\n control,\n visibleOptions,\n focusedIndex,\n activeDescendantId,\n rootClass,\n hasValue,\n displayLabel,\n canClear,\n toggle,\n selectOption,\n clearSelection,\n onOptionMouseenter,\n onTriggerKeydown,\n} = useSelect(props, (value) => {\n emit('update:modelValue', value);\n});\n\nvoid selectRef;\nvoid triggerRef;\n</script>\n\n<style src=\"./select.scss\" lang=\"scss\" scoped></style>\n","<template>\n <div :class=\"rootClass\" ref=\"selectRef\">\n <input\n v-if=\"name\"\n type=\"hidden\"\n :name=\"name\"\n :value=\"modelValue ?? ''\"\n :disabled=\"control.disabled || undefined\"\n />\n <div\n :id=\"control.id\"\n ref=\"triggerRef\"\n class=\"rp-select__trigger\"\n role=\"combobox\"\n :aria-expanded=\"isOpen\"\n aria-haspopup=\"listbox\"\n :aria-activedescendant=\"activeDescendantId\"\n :aria-controls=\"listboxId\"\n :aria-disabled=\"control.disabled || undefined\"\n :aria-invalid=\"control.invalid || undefined\"\n :aria-required=\"control.required || undefined\"\n :aria-label=\"ariaLabel || undefined\"\n :aria-labelledby=\"control.ariaLabelledby\"\n :aria-describedby=\"control.ariaDescribedby\"\n :data-state=\"isOpen ? 'open' : 'closed'\"\n :data-disabled=\"control.disabled || undefined\"\n :tabindex=\"control.disabled ? -1 : 0\"\n @click=\"toggle\"\n @keydown=\"onTriggerKeydown\"\n >\n <span class=\"rp-select__value\" :class=\"{ 'rp-select__placeholder': !hasValue }\">\n {{ displayLabel || placeholder }}\n </span>\n\n <span class=\"rp-select__indicator\">\n <button\n v-if=\"canClear\"\n type=\"button\"\n class=\"rp-select__clear\"\n aria-label=\"Clear selection\"\n tabindex=\"-1\"\n @mousedown.prevent\n @click.stop=\"clearSelection\"\n >\n <XIcon />\n </button>\n <span class=\"rp-select__arrow\" aria-hidden=\"true\">\n <ChevronsUpDownIcon />\n </span>\n </span>\n </div>\n\n <Transition name=\"rp-select-dropdown\">\n <div v-if=\"isOpen\" class=\"rp-select__dropdown\" role=\"listbox\" :id=\"listboxId\">\n <div v-if=\"visibleOptions.length === 0\" class=\"rp-select__empty\">\n <slot name=\"empty\">No options</slot>\n </div>\n <div\n v-for=\"(option, index) in visibleOptions\"\n :key=\"option.value\"\n role=\"option\"\n :id=\"`${selectId}-option-${index}`\"\n :aria-selected=\"option.value === modelValue\"\n :aria-disabled=\"option.disabled || undefined\"\n :data-selected=\"option.value === modelValue || undefined\"\n :data-disabled=\"option.disabled || undefined\"\n :class=\"[\n 'rp-select__option',\n {\n 'rp-select__option--selected': option.value === modelValue,\n 'rp-select__option--focused': index === focusedIndex,\n 'rp-select__option--disabled': option.disabled,\n },\n ]\"\n @click=\"selectOption(option)\"\n @mouseenter=\"onOptionMouseenter(option, index)\"\n >\n {{ option.label }}\n </div>\n </div>\n </Transition>\n </div>\n</template>\n\n<script lang=\"ts\" setup vapor>\nimport ChevronsUpDownIcon from '~icons/lucide/chevrons-up-down';\nimport XIcon from '~icons/lucide/x';\nimport { useSelect } from './useSelect';\nimport type { SelectProps } from './types';\n\ndefineOptions({ name: 'RpSelect' });\n\nconst props = withDefaults(defineProps<SelectProps>(), {\n options: () => [],\n placeholder: 'Select...',\n clearable: false,\n disabled: undefined,\n required: undefined,\n invalid: undefined,\n});\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string | number | null];\n}>();\n\nconst {\n selectRef,\n triggerRef,\n isOpen,\n selectId,\n listboxId,\n control,\n visibleOptions,\n focusedIndex,\n activeDescendantId,\n rootClass,\n hasValue,\n displayLabel,\n canClear,\n toggle,\n selectOption,\n clearSelection,\n onOptionMouseenter,\n onTriggerKeydown,\n} = useSelect(props, (value) => {\n emit('update:modelValue', value);\n});\n\nvoid selectRef;\nvoid triggerRef;\n</script>\n\n<style src=\"./select.scss\" lang=\"scss\" scoped></style>\n"],"mappings":"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"}
package/dist/switch.css CHANGED
@@ -1,23 +1,27 @@
1
- .rp-switch[data-v-8d0cc56f] {
1
+ .rp-switch[data-v-aeef0d94] {
2
+ --_switch-color: var(--rp-color-control-selected-bg);
3
+ --_switch-font-size: var(--rp-font-size-base);
4
+ --_track-w: 40px;
5
+ --_track-h: 22px;
6
+ --_thumb-size: 18px;
7
+ --_thumb-offset: 2px;
8
+ --_track-bg: var(--rp-color-control-track-bg);
9
+ --_thumb-bg: var(--rp-color-control-thumb-bg);
2
10
  display: inline-flex;
3
11
  align-items: center;
4
12
  cursor: pointer;
5
13
  font-family: var(--rp-font-family);
6
14
  user-select: none;
7
15
  }
8
- .rp-switch--disabled[data-v-8d0cc56f] {
16
+ .rp-switch--disabled[data-v-aeef0d94] {
9
17
  opacity: var(--rp-opacity-disabled);
10
18
  cursor: not-allowed;
11
19
  }
12
- .rp-switch[data-v-8d0cc56f] {
20
+ .rp-switch[data-v-aeef0d94] {
13
21
  gap: var(--rp-spacing-2);
14
- font-size: var(--rp-font-size-base);
15
- --_track-w: 40px;
16
- --_track-h: 22px;
17
- --_thumb-size: 18px;
18
- --_thumb-offset: 2px;
22
+ font-size: var(--_switch-font-size);
19
23
  }
20
- .rp-switch__native[data-v-8d0cc56f] {
24
+ .rp-switch__native[data-v-aeef0d94] {
21
25
  position: absolute;
22
26
  width: 1px;
23
27
  height: 1px;
@@ -29,42 +33,93 @@
29
33
  border-width: 0;
30
34
  pointer-events: none;
31
35
  }
32
- .rp-switch__native:focus-visible + .rp-switch__track[data-v-8d0cc56f] {
33
- border-color: var(--rp-color-control-border-focus);
36
+ .rp-switch__native:focus-visible + .rp-switch__track[data-v-aeef0d94] {
37
+ border-color: var(--rp-color-primary);
34
38
  box-shadow: 0 0 0 var(--rp-border-width-medium) var(--rp-color-focus-ring);
35
39
  }
36
- .rp-switch__track[data-v-8d0cc56f] {
40
+ .rp-switch__track[data-v-aeef0d94] {
37
41
  position: relative;
38
42
  display: inline-flex;
39
43
  align-items: center;
40
44
  width: var(--_track-w);
41
45
  height: var(--_track-h);
42
46
  flex-shrink: 0;
43
- background-color: var(--rp-color-control-track-bg);
47
+ background-color: var(--_track-bg);
44
48
  border-radius: var(--rp-radius-full);
45
49
  transition: background-color var(--rp-transition-fast);
46
50
  }
47
- .rp-switch--checked .rp-switch__track[data-v-8d0cc56f] {
48
- background-color: var(--rp-color-control-selected-bg);
51
+ .rp-switch--checked .rp-switch__track[data-v-aeef0d94] {
52
+ background-color: var(--_switch-color);
49
53
  }
50
- .rp-switch--invalid .rp-switch__track[data-v-8d0cc56f] {
54
+ .rp-switch--invalid .rp-switch__track[data-v-aeef0d94] {
51
55
  outline: var(--rp-border-width-thin) solid var(--rp-color-danger-fg);
52
56
  outline-offset: var(--_thumb-offset);
53
57
  }
54
- .rp-switch__thumb[data-v-8d0cc56f] {
58
+ .rp-switch__thumb[data-v-aeef0d94] {
55
59
  position: absolute;
56
60
  left: var(--_thumb-offset);
57
61
  width: var(--_thumb-size);
58
62
  height: var(--_thumb-size);
59
- background-color: var(--rp-color-control-thumb-bg);
63
+ background-color: var(--_thumb-bg);
60
64
  border-radius: var(--rp-radius-full);
61
65
  box-shadow: var(--rp-shadow-sm);
62
66
  transition: transform var(--rp-transition-fast);
63
67
  }
64
- .rp-switch--checked .rp-switch__thumb[data-v-8d0cc56f] {
68
+ .rp-switch--checked .rp-switch__thumb[data-v-aeef0d94] {
65
69
  transform: translateX(calc(var(--_track-w) - var(--_thumb-size) - var(--_thumb-offset) * 2));
66
70
  }
67
- .rp-switch__label[data-v-8d0cc56f] {
71
+ .rp-switch__label[data-v-aeef0d94] {
68
72
  line-height: var(--rp-line-height-normal);
69
73
  color: var(--rp-color-text);
74
+ }
75
+ .rp-switch--color-primary[data-v-aeef0d94] {
76
+ --_switch-color: var(--rp-color-primary);
77
+ }
78
+ .rp-switch--color-secondary[data-v-aeef0d94] {
79
+ --_switch-color: var(--rp-color-secondary);
80
+ }
81
+ .rp-switch--color-success[data-v-aeef0d94] {
82
+ --_switch-color: var(--rp-color-success);
83
+ }
84
+ .rp-switch--color-warning[data-v-aeef0d94] {
85
+ --_switch-color: var(--rp-color-warning);
86
+ }
87
+ .rp-switch--color-danger[data-v-aeef0d94] {
88
+ --_switch-color: var(--rp-color-danger);
89
+ }
90
+ .rp-switch--color-info[data-v-aeef0d94] {
91
+ --_switch-color: var(--rp-color-info);
92
+ }
93
+ .rp-switch--color-neutral[data-v-aeef0d94] {
94
+ --_switch-color: var(--rp-color-neutral);
95
+ }
96
+ .rp-switch--size-xs[data-v-aeef0d94] {
97
+ --_track-w: 28px;
98
+ --_track-h: 16px;
99
+ --_thumb-size: 12px;
100
+ --_switch-font-size: var(--rp-font-size-xs);
101
+ }
102
+ .rp-switch--size-sm[data-v-aeef0d94] {
103
+ --_track-w: 34px;
104
+ --_track-h: 18px;
105
+ --_thumb-size: 14px;
106
+ --_switch-font-size: var(--rp-font-size-sm);
107
+ }
108
+ .rp-switch--size-md[data-v-aeef0d94] {
109
+ --_track-w: 40px;
110
+ --_track-h: 22px;
111
+ --_thumb-size: 18px;
112
+ --_switch-font-size: var(--rp-font-size-base);
113
+ }
114
+ .rp-switch--size-lg[data-v-aeef0d94] {
115
+ --_track-w: 48px;
116
+ --_track-h: 26px;
117
+ --_thumb-size: 22px;
118
+ --_switch-font-size: var(--rp-font-size-lg);
119
+ }
120
+ .rp-switch--size-xl[data-v-aeef0d94] {
121
+ --_track-w: 56px;
122
+ --_track-h: 30px;
123
+ --_thumb-size: 26px;
124
+ --_switch-font-size: var(--rp-font-size-xl);
70
125
  }
package/dist/switch.js CHANGED
@@ -8,7 +8,9 @@ function useSwitch(props, emitUpdate) {
8
8
  const rootClass = computed(() => bem("rp-switch", {
9
9
  checked: props.modelValue,
10
10
  disabled: control.disabled,
11
- invalid: control.invalid
11
+ invalid: control.invalid,
12
+ [`color-${props.color}`]: Boolean(props.color),
13
+ [`size-${props.size}`]: Boolean(props.size)
12
14
  }));
13
15
  function onChange(e) {
14
16
  emitUpdate(e.target.checked);
@@ -21,8 +23,8 @@ function useSwitch(props, emitUpdate) {
21
23
  }
22
24
  //#endregion
23
25
  //#region src/components/switch/switch.vue?vue&type=script&setup=true&vapor=true&lang.ts
24
- var t0 = template("<span data-v-8d0cc56f class=rp-switch__label>");
25
- var t1 = template("<label data-v-8d0cc56f><input data-v-8d0cc56f type=checkbox class=rp-switch__native role=switch><span data-v-8d0cc56f class=rp-switch__track><span data-v-8d0cc56f class=rp-switch__thumb></span></span>", 1);
26
+ var t0 = template("<span data-v-aeef0d94 class=rp-switch__label>");
27
+ var t1 = template("<label data-v-aeef0d94><input data-v-aeef0d94 type=checkbox class=rp-switch__native role=switch><span data-v-aeef0d94 class=rp-switch__track><span data-v-aeef0d94 class=rp-switch__thumb></span></span>", 1);
26
28
  //#endregion
27
29
  //#region src/components/switch/switch.vue
28
30
  var switch_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ */ defineVaporComponent({
@@ -32,6 +34,8 @@ var switch_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE_
32
34
  id: {},
33
35
  name: {},
34
36
  modelValue: { type: Boolean },
37
+ color: {},
38
+ size: {},
35
39
  disabled: {
36
40
  type: Boolean,
37
41
  default: void 0
@@ -87,7 +91,7 @@ var switch_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE_
87
91
  });
88
92
  return n5;
89
93
  }
90
- }), [["__scopeId", "data-v-8d0cc56f"]]);
94
+ }), [["__scopeId", "data-v-aeef0d94"]]);
91
95
  //#endregion
92
96
  export { switch_default as t };
93
97
 
@@ -1 +1 @@
1
- {"version":3,"file":"switch.js","names":[],"sources":["../src/components/switch/useSwitch.ts","../src/components/switch/switch.vue","../src/components/switch/switch.vue"],"sourcesContent":["import { computed } from 'vue';\nimport { useControlState } from '@/composables/useControlState';\nimport { bem } from '@/utils/bem';\nimport type { SwitchProps } from './types';\n\nexport function useSwitch(\n props: Readonly<SwitchProps>,\n emitUpdate: (value: boolean) => void,\n) {\n const control = useControlState(props);\n\n const rootClass = computed(() =>\n bem('rp-switch', {\n checked: props.modelValue,\n disabled: control.disabled,\n invalid: control.invalid,\n }),\n );\n\n function onChange(e: Event) {\n emitUpdate((e.target as HTMLInputElement).checked);\n }\n\n return {\n control,\n rootClass,\n onChange,\n };\n}\n","<template>\n <label :class=\"rootClass\" :data-disabled=\"control.disabled || undefined\" :data-state=\"modelValue ? 'checked' : 'unchecked'\">\n <input\n :id=\"control.id\"\n :name=\"name\"\n type=\"checkbox\"\n class=\"rp-switch__native\"\n role=\"switch\"\n :checked=\"modelValue\"\n :disabled=\"control.disabled || undefined\"\n :required=\"control.required || undefined\"\n :aria-checked=\"modelValue\"\n :aria-label=\"ariaLabel || undefined\"\n :aria-labelledby=\"control.ariaLabelledby\"\n :aria-describedby=\"control.ariaDescribedby\"\n :aria-invalid=\"control.invalid || undefined\"\n :aria-required=\"control.required || undefined\"\n @change=\"onChange\"\n />\n <span class=\"rp-switch__track\">\n <span class=\"rp-switch__thumb\" />\n </span>\n <span v-if=\"$slots.default\" class=\"rp-switch__label\">\n <slot />\n </span>\n </label>\n</template>\n\n<script lang=\"ts\" setup vapor>\nimport { useSwitch } from './useSwitch';\nimport type { SwitchProps } from './types';\n\ndefineOptions({ name: 'RpSwitch' });\n\nconst props = withDefaults(defineProps<SwitchProps>(), {\n disabled: undefined,\n required: undefined,\n invalid: undefined,\n});\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: boolean];\n}>();\n\nconst { control, rootClass, onChange } = useSwitch(props, (value) => {\n emit('update:modelValue', value);\n});\n</script>\n\n<style src=\"./switch.scss\" lang=\"scss\" scoped></style>\n","<template>\n <label :class=\"rootClass\" :data-disabled=\"control.disabled || undefined\" :data-state=\"modelValue ? 'checked' : 'unchecked'\">\n <input\n :id=\"control.id\"\n :name=\"name\"\n type=\"checkbox\"\n class=\"rp-switch__native\"\n role=\"switch\"\n :checked=\"modelValue\"\n :disabled=\"control.disabled || undefined\"\n :required=\"control.required || undefined\"\n :aria-checked=\"modelValue\"\n :aria-label=\"ariaLabel || undefined\"\n :aria-labelledby=\"control.ariaLabelledby\"\n :aria-describedby=\"control.ariaDescribedby\"\n :aria-invalid=\"control.invalid || undefined\"\n :aria-required=\"control.required || undefined\"\n @change=\"onChange\"\n />\n <span class=\"rp-switch__track\">\n <span class=\"rp-switch__thumb\" />\n </span>\n <span v-if=\"$slots.default\" class=\"rp-switch__label\">\n <slot />\n </span>\n </label>\n</template>\n\n<script lang=\"ts\" setup vapor>\nimport { useSwitch } from './useSwitch';\nimport type { SwitchProps } from './types';\n\ndefineOptions({ name: 'RpSwitch' });\n\nconst props = withDefaults(defineProps<SwitchProps>(), {\n disabled: undefined,\n required: undefined,\n invalid: undefined,\n});\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: boolean];\n}>();\n\nconst { control, rootClass, onChange } = useSwitch(props, (value) => {\n emit('update:modelValue', value);\n});\n</script>\n\n<style src=\"./switch.scss\" lang=\"scss\" scoped></style>\n"],"mappings":";;;;AAKA,SAAgB,UACZ,OACA,YACF;CACE,MAAM,UAAU,gBAAgB,KAAK;CAErC,MAAM,YAAY,eACd,IAAI,aAAa;EACb,SAAS,MAAM;EACf,UAAU,QAAQ;EAClB,SAAS,QAAQ;CACrB,CAAC,CACL;CAEA,SAAS,SAAS,GAAU;EACxB,WAAY,EAAE,OAA4B,OAAO;CACrD;CAEA,OAAO;EACH;EACA;EACA;CACJ;AACJ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ECMA,MAAM,QAAQ;EAMd,MAAM,OAAO;EAIb,MAAM,EAAE,SAAS,WAAW,aAAa,UAAU,QAAQ,UAAU;GACjE,KAAK,qBAAqB,KAAK;EACnC,CAAC;;;SA7BY,WAAM,MAAE,MAAA,QAAA,CAAQ,CAAA,CAAA,CAAA;;;;;;gBAhBV,MAAA,SAAA,CAAS;eAAG,iBAAe,qBAAoB,KAAA,CAAS;eAAG,cAAY,cAAU,YAAA,WAAA;eAEvF,MAAI,SAAO,EAAA;eACX,QAAM,QAAA,IAAI;eAIV,WAAS,WAAU;eACnB,YAAU,qBAAoB,KAAA,CAAS;eACvC,YAAU,qBAAoB,KAAA,CAAS;eACvC,gBAAc,WAAU;eACxB,cAAY,QAAA,aAAa,KAAA,CAAS;eAClC,mBAAiB,SAAO,cAAA;eACxB,oBAAkB,SAAO,eAAA;eACzB,gBAAc,SAAO,WAAY,KAAA,CAAS;eAC1C,iBAAe,qBAAoB,KAAA,CAAS;;;iBAMrC,OAAM,eAAA"}
1
+ {"version":3,"file":"switch.js","names":[],"sources":["../src/components/switch/useSwitch.ts","../src/components/switch/switch.vue","../src/components/switch/switch.vue"],"sourcesContent":["import { computed } from 'vue';\nimport { useControlState } from '@/composables/useControlState';\nimport { bem } from '@/utils/bem';\nimport type { SwitchProps } from './types';\n\nexport function useSwitch(props: Readonly<SwitchProps>, emitUpdate: (value: boolean) => void) {\n const control = useControlState(props);\n\n const rootClass = computed(() =>\n bem('rp-switch', {\n checked: props.modelValue,\n disabled: control.disabled,\n invalid: control.invalid,\n [`color-${props.color}`]: Boolean(props.color),\n [`size-${props.size}`]: Boolean(props.size),\n }),\n );\n\n function onChange(e: Event) {\n emitUpdate((e.target as HTMLInputElement).checked);\n }\n\n return {\n control,\n rootClass,\n onChange,\n };\n}\n","<template>\n <label\n :class=\"rootClass\"\n :data-disabled=\"control.disabled || undefined\"\n :data-state=\"modelValue ? 'checked' : 'unchecked'\"\n >\n <input\n :id=\"control.id\"\n :name=\"name\"\n type=\"checkbox\"\n class=\"rp-switch__native\"\n role=\"switch\"\n :checked=\"modelValue\"\n :disabled=\"control.disabled || undefined\"\n :required=\"control.required || undefined\"\n :aria-checked=\"modelValue\"\n :aria-label=\"ariaLabel || undefined\"\n :aria-labelledby=\"control.ariaLabelledby\"\n :aria-describedby=\"control.ariaDescribedby\"\n :aria-invalid=\"control.invalid || undefined\"\n :aria-required=\"control.required || undefined\"\n @change=\"onChange\"\n />\n <span class=\"rp-switch__track\">\n <span class=\"rp-switch__thumb\" />\n </span>\n <span v-if=\"$slots.default\" class=\"rp-switch__label\">\n <slot />\n </span>\n </label>\n</template>\n\n<script lang=\"ts\" setup vapor>\nimport { useSwitch } from './useSwitch';\nimport type { SwitchProps } from './types';\n\ndefineOptions({ name: 'RpSwitch' });\n\nconst props = withDefaults(defineProps<SwitchProps>(), {\n disabled: undefined,\n required: undefined,\n invalid: undefined,\n});\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: boolean];\n}>();\n\nconst { control, rootClass, onChange } = useSwitch(props, (value) => {\n emit('update:modelValue', value);\n});\n</script>\n\n<style src=\"./switch.scss\" lang=\"scss\" scoped></style>\n","<template>\n <label\n :class=\"rootClass\"\n :data-disabled=\"control.disabled || undefined\"\n :data-state=\"modelValue ? 'checked' : 'unchecked'\"\n >\n <input\n :id=\"control.id\"\n :name=\"name\"\n type=\"checkbox\"\n class=\"rp-switch__native\"\n role=\"switch\"\n :checked=\"modelValue\"\n :disabled=\"control.disabled || undefined\"\n :required=\"control.required || undefined\"\n :aria-checked=\"modelValue\"\n :aria-label=\"ariaLabel || undefined\"\n :aria-labelledby=\"control.ariaLabelledby\"\n :aria-describedby=\"control.ariaDescribedby\"\n :aria-invalid=\"control.invalid || undefined\"\n :aria-required=\"control.required || undefined\"\n @change=\"onChange\"\n />\n <span class=\"rp-switch__track\">\n <span class=\"rp-switch__thumb\" />\n </span>\n <span v-if=\"$slots.default\" class=\"rp-switch__label\">\n <slot />\n </span>\n </label>\n</template>\n\n<script lang=\"ts\" setup vapor>\nimport { useSwitch } from './useSwitch';\nimport type { SwitchProps } from './types';\n\ndefineOptions({ name: 'RpSwitch' });\n\nconst props = withDefaults(defineProps<SwitchProps>(), {\n disabled: undefined,\n required: undefined,\n invalid: undefined,\n});\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: boolean];\n}>();\n\nconst { control, rootClass, onChange } = useSwitch(props, (value) => {\n emit('update:modelValue', value);\n});\n</script>\n\n<style src=\"./switch.scss\" lang=\"scss\" scoped></style>\n"],"mappings":";;;;AAKA,SAAgB,UAAU,OAA8B,YAAsC;CAC1F,MAAM,UAAU,gBAAgB,KAAK;CAErC,MAAM,YAAY,eACd,IAAI,aAAa;EACb,SAAS,MAAM;EACf,UAAU,QAAQ;EAClB,SAAS,QAAQ;GAChB,SAAS,MAAM,UAAU,QAAQ,MAAM,KAAK;GAC5C,QAAQ,MAAM,SAAS,QAAQ,MAAM,IAAI;CAC9C,CAAC,CACL;CAEA,SAAS,SAAS,GAAU;EACxB,WAAY,EAAE,OAA4B,OAAO;CACrD;CAEA,OAAO;EACH;EACA;EACA;CACJ;AACJ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ECWA,MAAM,QAAQ;EAMd,MAAM,OAAO;EAIb,MAAM,EAAE,SAAS,WAAW,aAAa,UAAU,QAAQ,UAAU;GACjE,KAAK,qBAAqB,KAAK;EACnC,CAAC;;;SA7BY,WAAM,MAAE,MAAA,QAAA,CAAQ,CAAA,CAAA,CAAA;;;;;;gBAnBb,MAAA,SAAA,CAAS;eAChB,iBAAe,qBAAoB,KAAA,CAAS;eAC5C,cAAY,cAAU,YAAA,WAAA;eAGlB,MAAI,SAAO,EAAA;eACX,QAAM,QAAA,IAAI;eAIV,WAAS,WAAU;eACnB,YAAU,qBAAoB,KAAA,CAAS;eACvC,YAAU,qBAAoB,KAAA,CAAS;eACvC,gBAAc,WAAU;eACxB,cAAY,QAAA,aAAa,KAAA,CAAS;eAClC,mBAAiB,SAAO,cAAA;eACxB,oBAAkB,SAAO,eAAA;eACzB,gBAAc,SAAO,WAAY,KAAA,CAAS;eAC1C,iBAAe,qBAAoB,KAAA,CAAS;;;iBAMrC,OAAM,eAAA"}
package/dist/textarea.css CHANGED
@@ -1,36 +1,106 @@
1
- .rp-textarea[data-v-84020885] {
1
+ .rp-textarea[data-v-2c68871d] {
2
+ --_rp-textarea-padding-y: var(--rp-spacing-3);
3
+ --_rp-textarea-padding-x: var(--rp-spacing-3);
4
+ --_rp-textarea-font-size: var(--rp-font-size-base);
5
+ --_rp-textarea-radius: var(--rp-radius-sm);
6
+ --_rp-textarea-valid-border: var(--rp-color-success-border-hover);
7
+ --_rp-textarea-valid-ring: color-mix(in srgb, var(--rp-color-success) 32%, transparent);
8
+ --_rp-textarea-invalid-border: var(--rp-color-danger-border-hover);
9
+ --_rp-textarea-invalid-ring: color-mix(in srgb, var(--rp-color-danger) 32%, transparent);
2
10
  display: inline-flex;
11
+ box-sizing: border-box;
3
12
  font-family: var(--rp-font-family);
4
13
  font-weight: var(--rp-font-weight-normal);
5
14
  background-color: var(--rp-color-control-bg);
6
15
  border: var(--rp-border-width-thin) solid var(--rp-color-control-border);
16
+ cursor: text;
7
17
  transition: border-color var(--rp-transition-fast), box-shadow var(--rp-transition-fast);
8
18
  }
9
- .rp-textarea[data-v-84020885]:hover:not(.rp-textarea--disabled) {
19
+ .rp-textarea[data-v-2c68871d]:hover:not(.rp-textarea--disabled):not(.rp-textarea--invalid):not(.rp-textarea--valid) {
10
20
  border-color: var(--rp-color-control-border-hover);
11
21
  }
12
- .rp-textarea[data-v-84020885]:focus-within:not(.rp-textarea--disabled) {
22
+ .rp-textarea[data-v-2c68871d]:focus-within:not(.rp-textarea--disabled):not(.rp-textarea--invalid):not(.rp-textarea--valid) {
13
23
  border-color: var(--rp-color-control-border-focus);
14
24
  box-shadow: 0 0 0 var(--rp-border-width-medium) var(--rp-color-focus-ring);
15
25
  }
16
- .rp-textarea[data-v-84020885] {
17
- padding: var(--rp-spacing-3) var(--rp-spacing-4);
18
- font-size: var(--rp-font-size-base);
19
- border-radius: var(--rp-radius-lg);
26
+ .rp-textarea[data-v-2c68871d] {
27
+ padding: var(--_rp-textarea-padding-y) var(--_rp-textarea-padding-x);
28
+ font-size: var(--_rp-textarea-font-size);
29
+ border-radius: var(--_rp-textarea-radius);
20
30
  }
21
- .rp-textarea--disabled[data-v-84020885] {
31
+ .rp-textarea--size-xs[data-v-2c68871d] {
32
+ --_rp-textarea-padding-y: var(--rp-spacing-1);
33
+ --_rp-textarea-padding-x: var(--rp-spacing-1);
34
+ --_rp-textarea-font-size: var(--rp-font-size-xs);
35
+ }
36
+ .rp-textarea--size-sm[data-v-2c68871d] {
37
+ --_rp-textarea-padding-y: var(--rp-spacing-2);
38
+ --_rp-textarea-padding-x: var(--rp-spacing-2);
39
+ --_rp-textarea-font-size: var(--rp-font-size-sm);
40
+ }
41
+ .rp-textarea--size-md[data-v-2c68871d] {
42
+ --_rp-textarea-padding-y: var(--rp-spacing-3);
43
+ --_rp-textarea-padding-x: var(--rp-spacing-3);
44
+ --_rp-textarea-font-size: var(--rp-font-size-base);
45
+ }
46
+ .rp-textarea--size-lg[data-v-2c68871d] {
47
+ --_rp-textarea-padding-y: var(--rp-spacing-4);
48
+ --_rp-textarea-padding-x: var(--rp-spacing-4);
49
+ --_rp-textarea-font-size: var(--rp-font-size-lg);
50
+ }
51
+ .rp-textarea--size-xl[data-v-2c68871d] {
52
+ --_rp-textarea-padding-y: var(--rp-spacing-5);
53
+ --_rp-textarea-padding-x: var(--rp-spacing-5);
54
+ --_rp-textarea-font-size: var(--rp-font-size-lg);
55
+ }
56
+ .rp-textarea--radius-xs[data-v-2c68871d] {
57
+ --_rp-textarea-radius: var(--rp-radius-sm);
58
+ }
59
+ .rp-textarea--radius-sm[data-v-2c68871d] {
60
+ --_rp-textarea-radius: var(--rp-radius-sm);
61
+ }
62
+ .rp-textarea--radius-md[data-v-2c68871d] {
63
+ --_rp-textarea-radius: var(--rp-radius-md);
64
+ }
65
+ .rp-textarea--radius-lg[data-v-2c68871d] {
66
+ --_rp-textarea-radius: var(--rp-radius-lg);
67
+ }
68
+ .rp-textarea--radius-xl[data-v-2c68871d] {
69
+ --_rp-textarea-radius: var(--rp-radius-xl);
70
+ }
71
+ html.dark .rp-textarea[data-v-2c68871d] {
72
+ --_rp-textarea-valid-border: var(--rp-color-success-fg);
73
+ --_rp-textarea-valid-ring: color-mix(in srgb, var(--rp-color-success-fg) 32%, transparent);
74
+ --_rp-textarea-invalid-border: var(--rp-color-danger-fg);
75
+ --_rp-textarea-invalid-ring: color-mix(in srgb, var(--rp-color-danger-fg) 32%, transparent);
76
+ }
77
+ .rp-textarea--disabled[data-v-2c68871d] {
22
78
  opacity: var(--rp-opacity-disabled);
23
79
  cursor: not-allowed;
24
80
  }
25
- .rp-textarea--invalid[data-v-84020885] {
26
- border-color: var(--rp-color-danger-fg);
81
+ .rp-textarea--valid[data-v-2c68871d] {
82
+ border-color: var(--_rp-textarea-valid-border);
83
+ }
84
+ .rp-textarea--valid[data-v-2c68871d]:focus-within:not(.rp-textarea--disabled) {
85
+ border-color: var(--_rp-textarea-valid-border);
86
+ box-shadow: 0 0 0 var(--rp-border-width-medium) var(--_rp-textarea-valid-ring);
87
+ }
88
+ .rp-textarea--invalid[data-v-2c68871d] {
89
+ border-color: var(--_rp-textarea-invalid-border);
90
+ }
91
+ .rp-textarea--invalid[data-v-2c68871d]:focus-within:not(.rp-textarea--disabled) {
92
+ border-color: var(--_rp-textarea-invalid-border);
93
+ box-shadow: 0 0 0 var(--rp-border-width-medium) var(--_rp-textarea-invalid-ring);
27
94
  }
28
- .rp-textarea--readonly[data-v-84020885] {
95
+ .rp-textarea--readonly[data-v-2c68871d] {
29
96
  background-color: var(--rp-color-control-readonly-bg);
30
97
  }
31
- .rp-textarea__native[data-v-84020885] {
98
+ .rp-textarea__native[data-v-2c68871d] {
99
+ box-sizing: border-box;
32
100
  flex: 1;
101
+ width: 100%;
33
102
  min-width: 0;
103
+ padding: 0;
34
104
  border: none;
35
105
  outline: none;
36
106
  background: transparent;
@@ -38,9 +108,9 @@
38
108
  color: var(--rp-color-control-fg);
39
109
  line-height: var(--rp-line-height-relaxed);
40
110
  }
41
- .rp-textarea__native[data-v-84020885]::placeholder {
111
+ .rp-textarea__native[data-v-2c68871d]::placeholder {
42
112
  color: var(--rp-color-control-placeholder);
43
113
  }
44
- .rp-textarea__native[data-v-84020885]:disabled {
114
+ .rp-textarea__native[data-v-2c68871d]:disabled {
45
115
  cursor: not-allowed;
46
116
  }
package/dist/textarea.js CHANGED
@@ -3,26 +3,47 @@ import { n as bem, t as _plugin_vue_export_helper_default } from "./_plugin-vue_
3
3
  import { t as useControlState } from "./useControlState.js";
4
4
  import { child, computed, createInvoker, defineVaporComponent, delegateEvents, renderEffect, setAttr, setClass, setProp, setValue, template, unref } from "vue";
5
5
  //#region src/components/textarea/useTextarea.ts
6
+ function isInteractiveElement(target) {
7
+ return Boolean(target.closest([
8
+ "button",
9
+ "a[href]",
10
+ "input",
11
+ "select",
12
+ "textarea:not(.rp-textarea__native)",
13
+ "[contenteditable=\"true\"]",
14
+ "[tabindex]:not([tabindex=\"-1\"])"
15
+ ].join(",")));
16
+ }
6
17
  function useTextarea(props, emitUpdate) {
7
18
  const control = useControlState(props);
8
19
  const rootClass = computed(() => bem("rp-textarea", {
20
+ [`size-${props.size}`]: Boolean(props.size),
21
+ [`radius-${props.radius}`]: Boolean(props.radius),
9
22
  disabled: control.disabled,
10
23
  invalid: control.invalid,
24
+ valid: control.valid && !control.invalid,
11
25
  readonly: props.readonly
12
26
  }));
13
27
  function onInput(e) {
14
28
  emitUpdate(e.target.value);
15
29
  }
30
+ function focusTextarea(e) {
31
+ if (control.disabled) return;
32
+ const target = e.target;
33
+ if (target instanceof Element && isInteractiveElement(target)) return;
34
+ e.currentTarget.querySelector(".rp-textarea__native")?.focus();
35
+ }
16
36
  return {
17
37
  control,
18
38
  rootClass,
19
- onInput
39
+ onInput,
40
+ focusTextarea
20
41
  };
21
42
  }
22
43
  //#endregion
23
44
  //#region src/components/textarea/textarea.vue?vue&type=script&setup=true&vapor=true&lang.ts
24
- var t0 = template("<div data-v-84020885><textarea data-v-84020885 class=rp-textarea__native>", 1);
25
- delegateEvents("input");
45
+ var t0 = template("<div data-v-2c68871d><textarea data-v-2c68871d class=rp-textarea__native>", 1);
46
+ delegateEvents("input", "mousedown");
26
47
  //#endregion
27
48
  //#region src/components/textarea/textarea.vue
28
49
  var textarea_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ */ defineVaporComponent({
@@ -32,6 +53,8 @@ var textarea_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PUR
32
53
  id: {},
33
54
  name: {},
34
55
  modelValue: {},
56
+ size: {},
57
+ radius: {},
35
58
  placeholder: { default: "" },
36
59
  rows: { default: 3 },
37
60
  disabled: {
@@ -50,6 +73,10 @@ var textarea_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PUR
50
73
  type: Boolean,
51
74
  default: void 0
52
75
  },
76
+ valid: {
77
+ type: Boolean,
78
+ default: void 0
79
+ },
53
80
  ariaLabel: {},
54
81
  describedby: {},
55
82
  labelledby: {}
@@ -58,12 +85,13 @@ var textarea_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PUR
58
85
  setup(__props, { emit: __emit }) {
59
86
  const props = __props;
60
87
  const emit = __emit;
61
- const { control, rootClass, onInput } = useTextarea(props, (value) => {
88
+ const { control, rootClass, onInput, focusTextarea } = useTextarea(props, (value) => {
62
89
  emit("update:modelValue", value);
63
90
  });
64
91
  const n1 = t0();
65
92
  const n0 = child(n1);
66
93
  n0.$evtinput = createInvoker((e) => unref(onInput)(e));
94
+ n1.$evtmousedown = createInvoker((e) => unref(focusTextarea)(e));
67
95
  renderEffect(() => {
68
96
  const _control = unref(control);
69
97
  const _control_required = _control.required;
@@ -84,7 +112,7 @@ var textarea_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PUR
84
112
  });
85
113
  return n1;
86
114
  }
87
- }), [["__scopeId", "data-v-84020885"]]);
115
+ }), [["__scopeId", "data-v-2c68871d"]]);
88
116
  //#endregion
89
117
  export { textarea_default as t };
90
118
 
@@ -1 +1 @@
1
- {"version":3,"file":"textarea.js","names":[],"sources":["../src/components/textarea/useTextarea.ts","../src/components/textarea/textarea.vue","../src/components/textarea/textarea.vue"],"sourcesContent":["import { computed } from 'vue';\nimport { useControlState } from '@/composables/useControlState';\nimport { bem } from '@/utils/bem';\nimport type { TextareaProps } from './types';\n\nexport function useTextarea(\n props: Readonly<TextareaProps>,\n emitUpdate: (value: string) => void,\n) {\n const control = useControlState(props);\n\n const rootClass = computed(() =>\n bem('rp-textarea', {\n disabled: control.disabled,\n invalid: control.invalid,\n readonly: props.readonly,\n }),\n );\n\n function onInput(e: Event) {\n emitUpdate((e.target as HTMLTextAreaElement).value);\n }\n\n return {\n control,\n rootClass,\n onInput,\n };\n}\n","<template>\n <div :class=\"rootClass\">\n <textarea\n :id=\"control.id\"\n :name=\"name\"\n class=\"rp-textarea__native\"\n :value=\"modelValue\"\n :placeholder=\"placeholder\"\n :rows=\"rows\"\n :disabled=\"control.disabled || undefined\"\n :readonly=\"readonly || undefined\"\n :required=\"control.required || undefined\"\n :aria-label=\"ariaLabel || undefined\"\n :aria-labelledby=\"control.ariaLabelledby\"\n :aria-describedby=\"control.ariaDescribedby\"\n :aria-invalid=\"control.invalid || undefined\"\n :aria-required=\"control.required || undefined\"\n @input=\"onInput\"\n />\n </div>\n</template>\n\n<script lang=\"ts\" setup vapor>\nimport { useTextarea } from './useTextarea';\nimport type { TextareaProps } from './types';\n\ndefineOptions({ name: 'RpTextarea' });\n\nconst props = withDefaults(defineProps<TextareaProps>(), {\n placeholder: '',\n rows: 3,\n disabled: undefined,\n required: undefined,\n invalid: undefined,\n readonly: false,\n});\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string];\n}>();\n\nconst { control, rootClass, onInput } = useTextarea(props, (value) => {\n emit('update:modelValue', value);\n});\n</script>\n\n<style src=\"./textarea.scss\" lang=\"scss\" scoped></style>\n","<template>\n <div :class=\"rootClass\">\n <textarea\n :id=\"control.id\"\n :name=\"name\"\n class=\"rp-textarea__native\"\n :value=\"modelValue\"\n :placeholder=\"placeholder\"\n :rows=\"rows\"\n :disabled=\"control.disabled || undefined\"\n :readonly=\"readonly || undefined\"\n :required=\"control.required || undefined\"\n :aria-label=\"ariaLabel || undefined\"\n :aria-labelledby=\"control.ariaLabelledby\"\n :aria-describedby=\"control.ariaDescribedby\"\n :aria-invalid=\"control.invalid || undefined\"\n :aria-required=\"control.required || undefined\"\n @input=\"onInput\"\n />\n </div>\n</template>\n\n<script lang=\"ts\" setup vapor>\nimport { useTextarea } from './useTextarea';\nimport type { TextareaProps } from './types';\n\ndefineOptions({ name: 'RpTextarea' });\n\nconst props = withDefaults(defineProps<TextareaProps>(), {\n placeholder: '',\n rows: 3,\n disabled: undefined,\n required: undefined,\n invalid: undefined,\n readonly: false,\n});\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string];\n}>();\n\nconst { control, rootClass, onInput } = useTextarea(props, (value) => {\n emit('update:modelValue', value);\n});\n</script>\n\n<style src=\"./textarea.scss\" lang=\"scss\" scoped></style>\n"],"mappings":";;;;AAKA,SAAgB,YACZ,OACA,YACF;CACE,MAAM,UAAU,gBAAgB,KAAK;CAErC,MAAM,YAAY,eACd,IAAI,eAAe;EACf,UAAU,QAAQ;EAClB,SAAS,QAAQ;EACjB,UAAU,MAAM;CACpB,CAAC,CACL;CAEA,SAAS,QAAQ,GAAU;EACvB,WAAY,EAAE,OAA+B,KAAK;CACtD;CAEA,OAAO;EACH;EACA;EACA;CACJ;AACJ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ECAA,MAAM,QAAQ;EASd,MAAM,OAAO;EAIb,MAAM,EAAE,SAAS,WAAW,YAAY,YAAY,QAAQ,UAAU;GAClE,KAAK,qBAAqB,KAAK;EACnC,CAAC;;;sCA1BmB,MAAA,OAAA,CAAO,CAAA,CAAA,CAAA;;;;gBAhBV,MAAA,SAAA,CAAS;eAEb,MAAI,SAAO,EAAA;eACX,QAAM,QAAA,IAAI;gBAEH,QAAA,UAAU;eACjB,eAAa,QAAA,WAAW;eACxB,QAAM,QAAA,IAAI;eACV,YAAU,SAAO,YAAa,KAAA,CAAS;eACvC,YAAU,QAAA,YAAY,KAAA,CAAS;eAC/B,YAAU,qBAAoB,KAAA,CAAS;eACvC,cAAY,QAAA,aAAa,KAAA,CAAS;eAClC,mBAAiB,SAAO,cAAA;eACxB,oBAAkB,SAAO,eAAA;eACzB,gBAAc,SAAO,WAAY,KAAA,CAAS;eAC1C,iBAAe,qBAAoB,KAAA,CAAS"}
1
+ {"version":3,"file":"textarea.js","names":[],"sources":["../src/components/textarea/useTextarea.ts","../src/components/textarea/textarea.vue","../src/components/textarea/textarea.vue"],"sourcesContent":["import { computed } from 'vue';\nimport { useControlState } from '@/composables/useControlState';\nimport { bem } from '@/utils/bem';\nimport type { TextareaProps } from './types';\n\nfunction isInteractiveElement(target: Element) {\n return Boolean(\n target.closest(\n [\n 'button',\n 'a[href]',\n 'input',\n 'select',\n 'textarea:not(.rp-textarea__native)',\n '[contenteditable=\"true\"]',\n '[tabindex]:not([tabindex=\"-1\"])',\n ].join(','),\n ),\n );\n}\n\nexport function useTextarea(props: Readonly<TextareaProps>, emitUpdate: (value: string) => void) {\n const control = useControlState(props);\n\n const rootClass = computed(() =>\n bem('rp-textarea', {\n [`size-${props.size}`]: Boolean(props.size),\n [`radius-${props.radius}`]: Boolean(props.radius),\n disabled: control.disabled,\n invalid: control.invalid,\n valid: control.valid && !control.invalid,\n readonly: props.readonly,\n }),\n );\n\n function onInput(e: Event) {\n emitUpdate((e.target as HTMLTextAreaElement).value);\n }\n\n function focusTextarea(e: MouseEvent) {\n if (control.disabled) return;\n\n const target = e.target;\n if (target instanceof Element && isInteractiveElement(target)) return;\n\n (e.currentTarget as HTMLElement)\n .querySelector<HTMLTextAreaElement>('.rp-textarea__native')\n ?.focus();\n }\n\n return {\n control,\n rootClass,\n onInput,\n focusTextarea,\n };\n}\n","<template>\n <div :class=\"rootClass\" @mousedown=\"focusTextarea\">\n <textarea\n :id=\"control.id\"\n :name=\"name\"\n class=\"rp-textarea__native\"\n :value=\"modelValue\"\n :placeholder=\"placeholder\"\n :rows=\"rows\"\n :disabled=\"control.disabled || undefined\"\n :readonly=\"readonly || undefined\"\n :required=\"control.required || undefined\"\n :aria-label=\"ariaLabel || undefined\"\n :aria-labelledby=\"control.ariaLabelledby\"\n :aria-describedby=\"control.ariaDescribedby\"\n :aria-invalid=\"control.invalid || undefined\"\n :aria-required=\"control.required || undefined\"\n @input=\"onInput\"\n />\n </div>\n</template>\n\n<script lang=\"ts\" setup vapor>\nimport { useTextarea } from './useTextarea';\nimport type { TextareaProps } from './types';\n\ndefineOptions({ name: 'RpTextarea' });\n\nconst props = withDefaults(defineProps<TextareaProps>(), {\n placeholder: '',\n rows: 3,\n disabled: undefined,\n required: undefined,\n invalid: undefined,\n valid: undefined,\n readonly: false,\n});\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string];\n}>();\n\nconst { control, rootClass, onInput, focusTextarea } = useTextarea(props, (value) => {\n emit('update:modelValue', value);\n});\n</script>\n\n<style src=\"./textarea.scss\" lang=\"scss\" scoped></style>\n","<template>\n <div :class=\"rootClass\" @mousedown=\"focusTextarea\">\n <textarea\n :id=\"control.id\"\n :name=\"name\"\n class=\"rp-textarea__native\"\n :value=\"modelValue\"\n :placeholder=\"placeholder\"\n :rows=\"rows\"\n :disabled=\"control.disabled || undefined\"\n :readonly=\"readonly || undefined\"\n :required=\"control.required || undefined\"\n :aria-label=\"ariaLabel || undefined\"\n :aria-labelledby=\"control.ariaLabelledby\"\n :aria-describedby=\"control.ariaDescribedby\"\n :aria-invalid=\"control.invalid || undefined\"\n :aria-required=\"control.required || undefined\"\n @input=\"onInput\"\n />\n </div>\n</template>\n\n<script lang=\"ts\" setup vapor>\nimport { useTextarea } from './useTextarea';\nimport type { TextareaProps } from './types';\n\ndefineOptions({ name: 'RpTextarea' });\n\nconst props = withDefaults(defineProps<TextareaProps>(), {\n placeholder: '',\n rows: 3,\n disabled: undefined,\n required: undefined,\n invalid: undefined,\n valid: undefined,\n readonly: false,\n});\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string];\n}>();\n\nconst { control, rootClass, onInput, focusTextarea } = useTextarea(props, (value) => {\n emit('update:modelValue', value);\n});\n</script>\n\n<style src=\"./textarea.scss\" lang=\"scss\" scoped></style>\n"],"mappings":";;;;AAKA,SAAS,qBAAqB,QAAiB;CAC3C,OAAO,QACH,OAAO,QACH;EACI;EACA;EACA;EACA;EACA;EACA;EACA;CACJ,CAAC,CAAC,KAAK,GAAG,CACd,CACJ;AACJ;AAEA,SAAgB,YAAY,OAAgC,YAAqC;CAC7F,MAAM,UAAU,gBAAgB,KAAK;CAErC,MAAM,YAAY,eACd,IAAI,eAAe;GACd,QAAQ,MAAM,SAAS,QAAQ,MAAM,IAAI;GACzC,UAAU,MAAM,WAAW,QAAQ,MAAM,MAAM;EAChD,UAAU,QAAQ;EAClB,SAAS,QAAQ;EACjB,OAAO,QAAQ,SAAS,CAAC,QAAQ;EACjC,UAAU,MAAM;CACpB,CAAC,CACL;CAEA,SAAS,QAAQ,GAAU;EACvB,WAAY,EAAE,OAA+B,KAAK;CACtD;CAEA,SAAS,cAAc,GAAe;EAClC,IAAI,QAAQ,UAAU;EAEtB,MAAM,SAAS,EAAE;EACjB,IAAI,kBAAkB,WAAW,qBAAqB,MAAM,GAAG;EAE/D,EAAG,cACE,cAAmC,sBAAsB,CAAC,EACzD,MAAM;CAChB;CAEA,OAAO;EACH;EACA;EACA;EACA;CACJ;AACJ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EC5BA,MAAM,QAAQ;EAUd,MAAM,OAAO;EAIb,MAAM,EAAE,SAAS,WAAW,SAAS,kBAAkB,YAAY,QAAQ,UAAU;GACjF,KAAK,qBAAqB,KAAK;EACnC,CAAC;;;sCA3BmB,MAAA,OAAA,CAAO,CAAA,CAAA,CAAA;0CAhBa,MAAA,aAAA,CAAa,CAAA,CAAA,CAAA;;;;gBAApC,MAAA,SAAA,CAAS;eAEb,MAAI,SAAO,EAAA;eACX,QAAM,QAAA,IAAI;gBAEH,QAAA,UAAU;eACjB,eAAa,QAAA,WAAW;eACxB,QAAM,QAAA,IAAI;eACV,YAAU,SAAO,YAAa,KAAA,CAAS;eACvC,YAAU,QAAA,YAAY,KAAA,CAAS;eAC/B,YAAU,qBAAoB,KAAA,CAAS;eACvC,cAAY,QAAA,aAAa,KAAA,CAAS;eAClC,mBAAiB,SAAO,cAAA;eACxB,oBAAkB,SAAO,eAAA;eACzB,gBAAc,SAAO,WAAY,KAAA,CAAS;eAC1C,iBAAe,qBAAoB,KAAA,CAAS"}
@@ -9,6 +9,7 @@ function useControlState(options = {}) {
9
9
  const disabled = computed(() => options.disabled ?? false);
10
10
  const required = computed(() => options.required ?? false);
11
11
  const invalid = computed(() => options.invalid ?? false);
12
+ const valid = computed(() => options.valid ?? false);
12
13
  const ariaDescribedby = computed(() => mergeIds(options.describedby));
13
14
  const ariaLabelledby = computed(() => mergeIds(options.labelledby));
14
15
  return {
@@ -24,6 +25,9 @@ function useControlState(options = {}) {
24
25
  get invalid() {
25
26
  return invalid.value;
26
27
  },
28
+ get valid() {
29
+ return valid.value;
30
+ },
27
31
  get ariaDescribedby() {
28
32
  return ariaDescribedby.value;
29
33
  },
@@ -1 +1 @@
1
- {"version":3,"file":"useControlState.js","names":[],"sources":["../src/composables/useControlState.ts"],"sourcesContent":["import { computed } from 'vue';\n\nexport interface ControlStateOptions {\n id?: string;\n disabled?: boolean;\n required?: boolean;\n invalid?: boolean;\n describedby?: string;\n labelledby?: string;\n}\n\nexport interface ControlState {\n id: string | undefined;\n disabled: boolean;\n required: boolean;\n invalid: boolean;\n ariaDescribedby: string | undefined;\n ariaLabelledby: string | undefined;\n}\n\nfunction mergeIds(...values: Array<string | undefined>): string | undefined {\n const ids = values\n .flatMap((value) => value?.split(/\\s+/) ?? [])\n .filter(Boolean);\n return ids.length > 0 ? Array.from(new Set(ids)).join(' ') : undefined;\n}\n\nexport function useControlState(options: ControlStateOptions = {}): ControlState {\n const id = computed(() => options.id);\n const disabled = computed(() => options.disabled ?? false);\n const required = computed(() => options.required ?? false);\n const invalid = computed(() => options.invalid ?? false);\n\n const ariaDescribedby = computed(() => mergeIds(options.describedby));\n const ariaLabelledby = computed(() => mergeIds(options.labelledby));\n\n return {\n get id() { return id.value; },\n get disabled() { return disabled.value; },\n get required() { return required.value; },\n get invalid() { return invalid.value; },\n get ariaDescribedby() { return ariaDescribedby.value; },\n get ariaLabelledby() { return ariaLabelledby.value; },\n };\n}\n"],"mappings":";;AAoBA,SAAS,SAAS,GAAG,QAAuD;CACxE,MAAM,MAAM,OACP,SAAS,UAAU,OAAO,MAAM,KAAK,KAAK,CAAC,CAAC,CAAC,CAC7C,OAAO,OAAO;CACnB,OAAO,IAAI,SAAS,IAAI,MAAM,KAAK,IAAI,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,KAAA;AACjE;AAEA,SAAgB,gBAAgB,UAA+B,CAAC,GAAiB;CAC7E,MAAM,KAAK,eAAe,QAAQ,EAAE;CACpC,MAAM,WAAW,eAAe,QAAQ,YAAY,KAAK;CACzD,MAAM,WAAW,eAAe,QAAQ,YAAY,KAAK;CACzD,MAAM,UAAU,eAAe,QAAQ,WAAW,KAAK;CAEvD,MAAM,kBAAkB,eAAe,SAAS,QAAQ,WAAW,CAAC;CACpE,MAAM,iBAAiB,eAAe,SAAS,QAAQ,UAAU,CAAC;CAElE,OAAO;EACH,IAAI,KAAK;GAAE,OAAO,GAAG;EAAO;EAC5B,IAAI,WAAW;GAAE,OAAO,SAAS;EAAO;EACxC,IAAI,WAAW;GAAE,OAAO,SAAS;EAAO;EACxC,IAAI,UAAU;GAAE,OAAO,QAAQ;EAAO;EACtC,IAAI,kBAAkB;GAAE,OAAO,gBAAgB;EAAO;EACtD,IAAI,iBAAiB;GAAE,OAAO,eAAe;EAAO;CACxD;AACJ"}
1
+ {"version":3,"file":"useControlState.js","names":[],"sources":["../src/composables/useControlState.ts"],"sourcesContent":["import { computed } from 'vue';\n\nexport interface ControlStateOptions {\n id?: string;\n disabled?: boolean;\n required?: boolean;\n invalid?: boolean;\n valid?: boolean;\n describedby?: string;\n labelledby?: string;\n}\n\nexport interface ControlState {\n id: string | undefined;\n disabled: boolean;\n required: boolean;\n invalid: boolean;\n valid: boolean;\n ariaDescribedby: string | undefined;\n ariaLabelledby: string | undefined;\n}\n\nfunction mergeIds(...values: Array<string | undefined>): string | undefined {\n const ids = values.flatMap((value) => value?.split(/\\s+/) ?? []).filter(Boolean);\n return ids.length > 0 ? Array.from(new Set(ids)).join(' ') : undefined;\n}\n\nexport function useControlState(options: ControlStateOptions = {}): ControlState {\n const id = computed(() => options.id);\n const disabled = computed(() => options.disabled ?? false);\n const required = computed(() => options.required ?? false);\n const invalid = computed(() => options.invalid ?? false);\n const valid = computed(() => options.valid ?? false);\n\n const ariaDescribedby = computed(() => mergeIds(options.describedby));\n const ariaLabelledby = computed(() => mergeIds(options.labelledby));\n\n return {\n get id() {\n return id.value;\n },\n get disabled() {\n return disabled.value;\n },\n get required() {\n return required.value;\n },\n get invalid() {\n return invalid.value;\n },\n get valid() {\n return valid.value;\n },\n get ariaDescribedby() {\n return ariaDescribedby.value;\n },\n get ariaLabelledby() {\n return ariaLabelledby.value;\n },\n };\n}\n"],"mappings":";;AAsBA,SAAS,SAAS,GAAG,QAAuD;CACxE,MAAM,MAAM,OAAO,SAAS,UAAU,OAAO,MAAM,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,OAAO;CAC/E,OAAO,IAAI,SAAS,IAAI,MAAM,KAAK,IAAI,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,KAAA;AACjE;AAEA,SAAgB,gBAAgB,UAA+B,CAAC,GAAiB;CAC7E,MAAM,KAAK,eAAe,QAAQ,EAAE;CACpC,MAAM,WAAW,eAAe,QAAQ,YAAY,KAAK;CACzD,MAAM,WAAW,eAAe,QAAQ,YAAY,KAAK;CACzD,MAAM,UAAU,eAAe,QAAQ,WAAW,KAAK;CACvD,MAAM,QAAQ,eAAe,QAAQ,SAAS,KAAK;CAEnD,MAAM,kBAAkB,eAAe,SAAS,QAAQ,WAAW,CAAC;CACpE,MAAM,iBAAiB,eAAe,SAAS,QAAQ,UAAU,CAAC;CAElE,OAAO;EACH,IAAI,KAAK;GACL,OAAO,GAAG;EACd;EACA,IAAI,WAAW;GACX,OAAO,SAAS;EACpB;EACA,IAAI,WAAW;GACX,OAAO,SAAS;EACpB;EACA,IAAI,UAAU;GACV,OAAO,QAAQ;EACnB;EACA,IAAI,QAAQ;GACR,OAAO,MAAM;EACjB;EACA,IAAI,kBAAkB;GAClB,OAAO,gBAAgB;EAC3B;EACA,IAAI,iBAAiB;GACjB,OAAO,eAAe;EAC1B;CACJ;AACJ"}