@feugene/granularity 0.30.0 → 0.31.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.
- package/dist/chunks/{config-DO8mjeIo.js → config-CIkdsWUk.js} +6 -2
- package/dist/chunks/config-CIkdsWUk.js.map +1 -0
- package/dist/chunks/{grInputStyles-CAk3xKIb.js → grInputStyles-DgQYjJvD.js} +17 -1
- package/dist/chunks/{grInputStyles-CAk3xKIb.js.map → grInputStyles-DgQYjJvD.js.map} +1 -1
- package/dist/chunks/{granular-provider-CHFaw3wU.js → granular-provider-yJNX1g1G.js} +1 -1
- package/dist/chunks/{granular-provider-CHFaw3wU.js.map → granular-provider-yJNX1g1G.js.map} +1 -1
- package/dist/components/GrColorPicker/chunks/{GrColorPicker-CE-xtNP-.js → GrColorPicker-C7ifyQ0R.js} +1 -1
- package/dist/components/GrColorPicker/chunks/{GrColorPicker-CE-xtNP-.js.map → GrColorPicker-C7ifyQ0R.js.map} +1 -1
- package/dist/components/GrColorPicker/index.js +1 -1
- package/dist/components/GrDialogService/chunks/{GrDialogService-k4SYIv64.js → GrDialogService-6s2cQ-_6.js} +1 -1
- package/dist/components/GrDialogService/chunks/{GrDialogService-k4SYIv64.js.map → GrDialogService-6s2cQ-_6.js.map} +1 -1
- package/dist/components/GrDialogService/index.js +1 -1
- package/dist/components/GrInput/chunks/{GrInput-BqqoEl1b.js → GrInput-BLywRZUN.js} +10 -6
- package/dist/components/GrInput/chunks/GrInput-BLywRZUN.js.map +1 -0
- package/dist/components/GrInput/index.js +2 -2
- package/dist/components/GrJsonViewer/chunks/{GrJsonViewer-CgwjoUJt.js → GrJsonViewer-Cy2bknHO.js} +1 -1
- package/dist/components/GrJsonViewer/chunks/{GrJsonViewer-CgwjoUJt.js.map → GrJsonViewer-Cy2bknHO.js.map} +1 -1
- package/dist/components/GrJsonViewer/index.js +1 -1
- package/dist/components/GrPagination/chunks/{GrPagination-kYCx6PAS.js → GrPagination-Cw3R21dp.js} +1 -1
- package/dist/components/GrPagination/chunks/{GrPagination-kYCx6PAS.js.map → GrPagination-Cw3R21dp.js.map} +1 -1
- package/dist/components/GrPagination/index.js +1 -1
- package/dist/components/GrPromptDialog/chunks/{GrPromptDialog-D1ADRFxx.js → GrPromptDialog-DLcl1xiq.js} +1 -1
- package/dist/components/GrPromptDialog/chunks/{GrPromptDialog-D1ADRFxx.js.map → GrPromptDialog-DLcl1xiq.js.map} +1 -1
- package/dist/components/GrPromptDialog/index.js +1 -1
- package/dist/components/GrSelect/chunks/{GrSelect-CBp0twbF.js → GrSelect-BiUrSSxc.js} +1 -1
- package/dist/components/GrSelect/chunks/{GrSelect-CBp0twbF.js.map → GrSelect-BiUrSSxc.js.map} +1 -1
- package/dist/components/GrSelect/index.js +1 -1
- package/dist/components/GrTreeSelect/chunks/{GrTreeSelect-CymO68Cv.js → GrTreeSelect-iJnMl2t9.js} +1 -1
- package/dist/components/GrTreeSelect/chunks/{GrTreeSelect-CymO68Cv.js.map → GrTreeSelect-iJnMl2t9.js.map} +1 -1
- package/dist/components/GrTreeSelect/index.js +1 -1
- package/dist/granular-provider-node.js +1 -1
- package/dist/granular-provider.js +1 -1
- package/dist/index.js +9 -9
- package/dist/types/components/GrInput/GrInput.vue.d.ts +16 -0
- package/dist/types/components/GrInput/grInputStyles.d.ts +16 -0
- package/dist/web-types.json +10 -1
- package/package.json +1 -1
- package/dist/chunks/config-DO8mjeIo.js.map +0 -1
- package/dist/components/GrInput/chunks/GrInput-BqqoEl1b.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"GrInput-BqqoEl1b.js","names":["$slots","$attrs"],"sources":["../../../../src/components/GrInput/GrInput.vue","../../../../src/components/GrInput/GrInput.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { GrComponentSize } from '../shared/sizes'\nimport { computed, ref, useId } from 'vue'\n\nimport { useControlAddons } from '../../composables/internal/useControlAddons'\nimport { useGrComponentProp, useGrComponentSize } from '../GrConfigProvider/context'\nimport { useGrFormFieldContext } from '../GrFormField/context'\nimport { useGrFormControl } from '../../composables/useGrFormControl'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport type { InputHTMLAttributes } from 'vue'\n\nimport { grInputFieldClass, grInputShellClass, paddingX } from './grInputStyles'\n\nimport IconLoader from '~icons/lucide/loader-2'\nimport IconX from '~icons/lucide/x'\nimport IconEye from '~icons/lucide/eye'\nimport IconEyeOff from '~icons/lucide/eye-off'\n\nexport interface GrInputProps {\n /**\n * Значение поля. Необязательное: без `v-model` поле рисуется пустым.\n * Дефолт — пустая строка, а не `undefined`: длина значения читается\n * напрямую (`showClear`), и `undefined` уронил бы рендер.\n */\n modelValue?: string\n type?: 'text' | 'email' | 'password' | 'number' | 'search' | 'tel' | 'url'\n placeholder?: string\n autocomplete?: string\n inputmode?: InputHTMLAttributes['inputmode']\n disabled?: boolean\n /** Только для чтения: значение видно и выделяемо, но не редактируется. */\n readonly?: boolean\n invalid?: boolean\n /** Обязательное поле (`aria-required`). Складывается с `required` у `GrFormField`. */\n required?: boolean\n /** Доступное имя вне `GrFormField`. */\n ariaLabel?: string\n state?: 'default' | 'success' | 'warning' | 'danger'\n name?: string\n id?: string\n size?: GrInputSize\n\n /** Показывать кнопку очистки, когда есть значение (и не disabled/readonly). */\n clearable?: boolean\n /** i18n aria-label кнопки очистки. */\n clearLabel?: string\n /** Ограничение длины + основа для счётчика символов. */\n maxlength?: number\n /** Показывать счётчик символов (`len` или `len/maxlength`). */\n showCount?: boolean\n /**\n * Фоновая работа по полю (проверка занятости логина, автосохранение):\n * спиннер в trailing-области + `aria-busy`. Ввод не блокируется —\n * для этого есть `disabled`/`readonly`.\n */\n loading?: boolean\n /** Кнопка показать/скрыть пароль (только при `type=\"password\"`). */\n passwordToggle?: boolean\n /** i18n aria-label кнопки показать/скрыть пароль. */\n passwordShowLabel?: string\n passwordHideLabel?: string\n\n textAlign?: GrInputTextAlign\n\n prefixMinWidth?: string\n prefixMaxWidth?: string\n suffixMinWidth?: string\n suffixMaxWidth?: string\n /**\n * Фиксированная ширина у prefix/suffix: аддон получает жёсткую ширину\n * (из `*MaxWidth` → `*MinWidth` → дефолт), а контент обрезается по краю\n * (prefix — справа, suffix — слева). По умолчанию аддоны «растягиваются»\n * под контент (в пределах min/max), а излишек клипается оболочкой.\n */\n prefixFixed?: boolean\n suffixFixed?: boolean\n}\n\nexport interface GrInputEmits {\n (e: 'update:modelValue', value: string): void\n /** Значение зафиксировано нативным `change` — по `blur` или `Enter`. */\n (e: 'change', value: string): void\n (e: 'focus', event: FocusEvent): void\n (e: 'blur', event: FocusEvent): void\n /**\n * Значение стёрто кнопкой очистки. Отдельное событие потому, что по\n * `update:modelValue` программную очистку от ручного стирания не отличить.\n */\n (e: 'clear'): void\n}\n\ndefineOptions({\n inheritAttrs: false,\n})\n\nexport type GrInputSize = GrComponentSize\nexport type GrInputTextAlign = 'left' | 'center' | 'right'\n\nconst props = withDefaults(\n defineProps<GrInputProps>(),\n {\n modelValue: '',\n type: 'text',\n placeholder: undefined,\n autocomplete: undefined,\n inputmode: undefined,\n disabled: false,\n readonly: false,\n invalid: false,\n required: false,\n ariaLabel: undefined,\n state: 'default',\n name: undefined,\n id: undefined,\n size: undefined,\n\n // Настраивается через `GrConfigProvider`; дефолт — в резолвере ниже.\n clearable: undefined,\n clearLabel: undefined,\n maxlength: undefined,\n showCount: false,\n loading: false,\n passwordToggle: false,\n passwordShowLabel: undefined,\n passwordHideLabel: undefined,\n\n textAlign: 'left',\n\n prefixMinWidth: undefined,\n prefixMaxWidth: undefined,\n suffixMinWidth: undefined,\n suffixMaxWidth: undefined,\n prefixFixed: false,\n suffixFixed: false,\n },\n)\n\nconst emit = defineEmits<GrInputEmits>()\ndefineSlots<{\n /** Аддон слева от поля: иконка, код валюты, метка. */\n prefix?: () => any\n /** Аддон справа от поля: единица измерения, подсказка. */\n suffix?: () => any\n}>()\n\n\n// Контекст `GrFormField` (если инпут внутри него): даёт id/aria-describedby/\n// invalid/required как fallback, чтобы не прокидывать `forId` вручную.\nconst field = useGrFormFieldContext()\n\n// Эффективные значения: локальный проп → `GrConfigProvider` → дефолт компонента.\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrInput' })\nconst resolvedClearable = useGrComponentProp('GrInput', 'clearable', () => props.clearable, false)\n\nconst resolvedId = computed(() => props.id ?? field?.id.value)\n\n// Счётчик обязан быть частью описания поля: иначе «12 / 60» видно глазами, но\n// не слышно — при том, что ограничение длины и есть смысл счётчика.\nconst countId = useId()\nconst describedBy = computed(() =>\n [field?.describedById.value, props.showCount ? countId : undefined]\n .filter(Boolean)\n .join(' ') || undefined,\n)\nconst {\n disabled: isDisabled,\n invalid: isInvalid,\n required: isRequired,\n readonly: isReadonly,\n} = useGrFormControl(() => props)\n\nconst inputEl = ref<HTMLInputElement | null>(null)\n\nfunction focus(): void {\n inputEl.value?.focus()\n}\n\nfunction blur(): void {\n inputEl.value?.blur()\n}\n\n/** Выделить содержимое — спутник `focus()` для «подставили значение, перезапишите». */\nfunction select(): void {\n inputEl.value?.select()\n}\n\ndefineExpose({\n focus,\n blur,\n select,\n})\n\nconst ADDON_MIN_WIDTH_BY_SIZE: Record<GrInputSize, string> = {\n xs: '2rem', // w-8\n sm: '2.25rem', // w-9\n md: '2.5rem', // w-10\n lg: '3rem', // w-12\n}\n\nconst passwordVisible = ref(false)\n// Тип поля с учётом переключателя пароля.\nconst resolvedType = computed(() => (props.type === 'password' && passwordVisible.value ? 'text' : props.type))\n\nconst showPasswordToggle = computed(() => props.passwordToggle && props.type === 'password' && !isDisabled.value)\nconst showClear = computed(() => resolvedClearable.value && props.modelValue.length > 0 && !isDisabled.value && !isReadonly.value)\n\nconst trailingCount = computed(() => (showClear.value ? 1 : 0) + (showPasswordToggle.value ? 1 : 0) + (props.loading ? 1 : 0))\nconst trailingReserve = computed(() => (trailingCount.value > 0 ? `${trailingCount.value * 28}px` : '0px'))\n\nconst {\n prefixEl,\n suffixEl,\n prefixStyle,\n suffixStyle,\n fieldPadding: inputStyle,\n} = useControlAddons(() => props, {\n defaultMinWidth: () => ADDON_MIN_WIDTH_BY_SIZE[resolvedSize.value],\n paddingX: () => paddingX[resolvedSize.value],\n trailingReserve: () => trailingReserve.value,\n})\n\n// Border/ring/disabled — на оболочке (`focus-within`), размеры/выравнивание — на инпуте.\nconst shellClass = computed(() => grInputShellClass({\n state: props.state,\n invalid: isInvalid.value,\n disabled: isDisabled.value,\n}))\n\nconst className = computed(() => grInputFieldClass({\n size: resolvedSize.value,\n align: props.textAlign,\n}))\n\nfunction onInput(e: Event): void {\n emit('update:modelValue', (e.target as HTMLInputElement).value)\n}\n\n// Объявленный emit уходит из `$attrs`, поэтому нативные события переизлучаем\n// руками — иначе `@change`/`@focus`/`@blur` у потребителя перестали бы работать.\nfunction onChange(e: Event): void {\n emit('change', (e.target as HTMLInputElement).value)\n}\n\nfunction onFocus(e: FocusEvent): void {\n emit('focus', e)\n}\n\nfunction onBlur(e: FocusEvent): void {\n emit('blur', e)\n}\n\n// ————— Trailing-контролы: очистка, переключатель пароля, счётчик символов.\nconst { t } = useGranularityTranslations()\nconst resolvedClearLabel = computed(() => props.clearLabel ?? t('gr.input.clear', 'Clear'))\nconst resolvedPasswordShowLabel = computed(() => props.passwordShowLabel ?? t('gr.input.showPassword', 'Show password'))\nconst resolvedPasswordHideLabel = computed(() => props.passwordHideLabel ?? t('gr.input.hidePassword', 'Hide password'))\n\n// Счётчик символов: `len` или `len / maxlength`.\nconst countText = computed(() =>\n props.maxlength !== undefined ? `${props.modelValue.length} / ${props.maxlength}` : String(props.modelValue.length),\n)\n\n/**\n * Живой регион объявляет только исчерпание лимита. Читать вслух каждый символ\n * нельзя — диктор захлебнётся, а сам счётчик и так связан с полем через\n * `aria-describedby` и читается при фокусе.\n */\nconst limitReached = computed(() =>\n props.maxlength !== undefined && props.modelValue.length >= props.maxlength,\n)\nconst liveMessage = computed(() =>\n limitReached.value ? t('gr.input.limitReached', 'Character limit reached') : '',\n)\n\nfunction clear(): void {\n emit('update:modelValue', '')\n emit('clear')\n inputEl.value?.focus()\n}\n\nfunction togglePassword(): void {\n passwordVisible.value = !passwordVisible.value\n inputEl.value?.focus()\n}\n</script>\n\n<template>\n <div data-gr-input class=\"w-full\">\n <div :class=\"shellClass\">\n <div\n v-if=\"$slots.prefix\"\n ref=\"prefixEl\"\n data-testid=\"gr-input-prefix\"\n class=\"absolute inset-y-0 left-0 flex items-center justify-center border-r border-[var(--gr-brd)] px-2 text-[var(--gr-muted-fg)] pointer-events-none select-none truncate\"\n :style=\"prefixStyle\"\n aria-hidden=\"true\"\n >\n <slot name=\"prefix\" />\n </div>\n\n <input\n :id=\"resolvedId\"\n ref=\"inputEl\"\n v-bind=\"$attrs\"\n :name=\"props.name\"\n :type=\"resolvedType\"\n :inputmode=\"props.inputmode\"\n :autocomplete=\"props.autocomplete\"\n :placeholder=\"props.placeholder\"\n :disabled=\"isDisabled\"\n :readonly=\"isReadonly\"\n :maxlength=\"props.maxlength\"\n :value=\"props.modelValue\"\n :aria-invalid=\"isInvalid ? 'true' : undefined\"\n :aria-describedby=\"describedBy\"\n :aria-required=\"isRequired ? 'true' : undefined\"\n :aria-readonly=\"isReadonly ? 'true' : undefined\"\n :aria-label=\"ariaLabel\"\n :aria-busy=\"props.loading ? 'true' : undefined\"\n class=\"w-full bg-transparent text-[var(--gr-fg)] placeholder:text-[var(--gr-muted-fg)] focus:placeholder:text-transparent focus:outline-none disabled:cursor-not-allowed disabled:text-[var(--gr-muted-fg)]\"\n :class=\"className\"\n :style=\"inputStyle\"\n @input=\"onInput\"\n @change=\"onChange\"\n @focus=\"onFocus\"\n @blur=\"onBlur\"\n >\n\n <div\n v-if=\"trailingCount > 0\"\n data-gr-input-trailing\n class=\"absolute inset-y-0 right-1 flex items-center gap-0.5\"\n >\n <span\n v-if=\"loading\"\n data-gr-input-spinner\n class=\"flex h-6 w-6 items-center justify-center text-[var(--gr-muted-fg)]\"\n aria-hidden=\"true\"\n >\n <IconLoader class=\"h-4 w-4 animate-spin\" />\n </span>\n\n <button\n v-if=\"showClear\"\n type=\"button\"\n data-gr-input-clear\n :aria-label=\"resolvedClearLabel\"\n class=\"flex h-6 w-6 items-center justify-center rounded-[var(--gr-radius-sm)] text-[var(--gr-muted-fg)] transition-colors hover:bg-[var(--gr-muted)] hover:text-[var(--gr-fg)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--gr-ring)]\"\n @click=\"clear\"\n >\n <IconX class=\"h-4 w-4\" aria-hidden=\"true\" />\n </button>\n\n <button\n v-if=\"showPasswordToggle\"\n type=\"button\"\n data-gr-input-password-toggle\n :aria-label=\"passwordVisible ? resolvedPasswordHideLabel : resolvedPasswordShowLabel\"\n :aria-pressed=\"passwordVisible ? 'true' : 'false'\"\n class=\"flex h-6 w-6 items-center justify-center rounded-[var(--gr-radius-sm)] text-[var(--gr-muted-fg)] transition-colors hover:bg-[var(--gr-muted)] hover:text-[var(--gr-fg)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--gr-ring)]\"\n @click=\"togglePassword\"\n >\n <IconEyeOff v-if=\"passwordVisible\" class=\"h-4 w-4\" aria-hidden=\"true\" />\n <IconEye v-else class=\"h-4 w-4\" aria-hidden=\"true\" />\n </button>\n </div>\n\n <div\n v-if=\"$slots.suffix\"\n ref=\"suffixEl\"\n data-testid=\"gr-input-suffix\"\n class=\"absolute inset-y-0 right-0 flex items-center justify-center border-l border-[var(--gr-brd)] px-2 text-[var(--gr-muted-fg)] pointer-events-none select-none truncate\"\n :class=\"suffixFixed ? '[direction:rtl]' : ''\"\n :style=\"suffixStyle\"\n aria-hidden=\"true\"\n >\n <slot name=\"suffix\" />\n </div>\n </div>\n\n <div\n v-if=\"showCount\"\n :id=\"countId\"\n data-gr-input-count\n class=\"mt-1 text-right text-[length:var(--gr-control-text-xs)] leading-[var(--gr-leading-xs)] text-[var(--gr-muted-fg)] tabular-nums\"\n >\n {{ countText }}\n </div>\n\n <span\n v-if=\"showCount\"\n data-gr-input-live\n class=\"sr-only\"\n role=\"status\"\n aria-live=\"polite\"\n >{{ liveMessage }}</span>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport type { GrComponentSize } from '../shared/sizes'\nimport { computed, ref, useId } from 'vue'\n\nimport { useControlAddons } from '../../composables/internal/useControlAddons'\nimport { useGrComponentProp, useGrComponentSize } from '../GrConfigProvider/context'\nimport { useGrFormFieldContext } from '../GrFormField/context'\nimport { useGrFormControl } from '../../composables/useGrFormControl'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport type { InputHTMLAttributes } from 'vue'\n\nimport { grInputFieldClass, grInputShellClass, paddingX } from './grInputStyles'\n\nimport IconLoader from '~icons/lucide/loader-2'\nimport IconX from '~icons/lucide/x'\nimport IconEye from '~icons/lucide/eye'\nimport IconEyeOff from '~icons/lucide/eye-off'\n\nexport interface GrInputProps {\n /**\n * Значение поля. Необязательное: без `v-model` поле рисуется пустым.\n * Дефолт — пустая строка, а не `undefined`: длина значения читается\n * напрямую (`showClear`), и `undefined` уронил бы рендер.\n */\n modelValue?: string\n type?: 'text' | 'email' | 'password' | 'number' | 'search' | 'tel' | 'url'\n placeholder?: string\n autocomplete?: string\n inputmode?: InputHTMLAttributes['inputmode']\n disabled?: boolean\n /** Только для чтения: значение видно и выделяемо, но не редактируется. */\n readonly?: boolean\n invalid?: boolean\n /** Обязательное поле (`aria-required`). Складывается с `required` у `GrFormField`. */\n required?: boolean\n /** Доступное имя вне `GrFormField`. */\n ariaLabel?: string\n state?: 'default' | 'success' | 'warning' | 'danger'\n name?: string\n id?: string\n size?: GrInputSize\n\n /** Показывать кнопку очистки, когда есть значение (и не disabled/readonly). */\n clearable?: boolean\n /** i18n aria-label кнопки очистки. */\n clearLabel?: string\n /** Ограничение длины + основа для счётчика символов. */\n maxlength?: number\n /** Показывать счётчик символов (`len` или `len/maxlength`). */\n showCount?: boolean\n /**\n * Фоновая работа по полю (проверка занятости логина, автосохранение):\n * спиннер в trailing-области + `aria-busy`. Ввод не блокируется —\n * для этого есть `disabled`/`readonly`.\n */\n loading?: boolean\n /** Кнопка показать/скрыть пароль (только при `type=\"password\"`). */\n passwordToggle?: boolean\n /** i18n aria-label кнопки показать/скрыть пароль. */\n passwordShowLabel?: string\n passwordHideLabel?: string\n\n textAlign?: GrInputTextAlign\n\n prefixMinWidth?: string\n prefixMaxWidth?: string\n suffixMinWidth?: string\n suffixMaxWidth?: string\n /**\n * Фиксированная ширина у prefix/suffix: аддон получает жёсткую ширину\n * (из `*MaxWidth` → `*MinWidth` → дефолт), а контент обрезается по краю\n * (prefix — справа, suffix — слева). По умолчанию аддоны «растягиваются»\n * под контент (в пределах min/max), а излишек клипается оболочкой.\n */\n prefixFixed?: boolean\n suffixFixed?: boolean\n}\n\nexport interface GrInputEmits {\n (e: 'update:modelValue', value: string): void\n /** Значение зафиксировано нативным `change` — по `blur` или `Enter`. */\n (e: 'change', value: string): void\n (e: 'focus', event: FocusEvent): void\n (e: 'blur', event: FocusEvent): void\n /**\n * Значение стёрто кнопкой очистки. Отдельное событие потому, что по\n * `update:modelValue` программную очистку от ручного стирания не отличить.\n */\n (e: 'clear'): void\n}\n\ndefineOptions({\n inheritAttrs: false,\n})\n\nexport type GrInputSize = GrComponentSize\nexport type GrInputTextAlign = 'left' | 'center' | 'right'\n\nconst props = withDefaults(\n defineProps<GrInputProps>(),\n {\n modelValue: '',\n type: 'text',\n placeholder: undefined,\n autocomplete: undefined,\n inputmode: undefined,\n disabled: false,\n readonly: false,\n invalid: false,\n required: false,\n ariaLabel: undefined,\n state: 'default',\n name: undefined,\n id: undefined,\n size: undefined,\n\n // Настраивается через `GrConfigProvider`; дефолт — в резолвере ниже.\n clearable: undefined,\n clearLabel: undefined,\n maxlength: undefined,\n showCount: false,\n loading: false,\n passwordToggle: false,\n passwordShowLabel: undefined,\n passwordHideLabel: undefined,\n\n textAlign: 'left',\n\n prefixMinWidth: undefined,\n prefixMaxWidth: undefined,\n suffixMinWidth: undefined,\n suffixMaxWidth: undefined,\n prefixFixed: false,\n suffixFixed: false,\n },\n)\n\nconst emit = defineEmits<GrInputEmits>()\ndefineSlots<{\n /** Аддон слева от поля: иконка, код валюты, метка. */\n prefix?: () => any\n /** Аддон справа от поля: единица измерения, подсказка. */\n suffix?: () => any\n}>()\n\n\n// Контекст `GrFormField` (если инпут внутри него): даёт id/aria-describedby/\n// invalid/required как fallback, чтобы не прокидывать `forId` вручную.\nconst field = useGrFormFieldContext()\n\n// Эффективные значения: локальный проп → `GrConfigProvider` → дефолт компонента.\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrInput' })\nconst resolvedClearable = useGrComponentProp('GrInput', 'clearable', () => props.clearable, false)\n\nconst resolvedId = computed(() => props.id ?? field?.id.value)\n\n// Счётчик обязан быть частью описания поля: иначе «12 / 60» видно глазами, но\n// не слышно — при том, что ограничение длины и есть смысл счётчика.\nconst countId = useId()\nconst describedBy = computed(() =>\n [field?.describedById.value, props.showCount ? countId : undefined]\n .filter(Boolean)\n .join(' ') || undefined,\n)\nconst {\n disabled: isDisabled,\n invalid: isInvalid,\n required: isRequired,\n readonly: isReadonly,\n} = useGrFormControl(() => props)\n\nconst inputEl = ref<HTMLInputElement | null>(null)\n\nfunction focus(): void {\n inputEl.value?.focus()\n}\n\nfunction blur(): void {\n inputEl.value?.blur()\n}\n\n/** Выделить содержимое — спутник `focus()` для «подставили значение, перезапишите». */\nfunction select(): void {\n inputEl.value?.select()\n}\n\ndefineExpose({\n focus,\n blur,\n select,\n})\n\nconst ADDON_MIN_WIDTH_BY_SIZE: Record<GrInputSize, string> = {\n xs: '2rem', // w-8\n sm: '2.25rem', // w-9\n md: '2.5rem', // w-10\n lg: '3rem', // w-12\n}\n\nconst passwordVisible = ref(false)\n// Тип поля с учётом переключателя пароля.\nconst resolvedType = computed(() => (props.type === 'password' && passwordVisible.value ? 'text' : props.type))\n\nconst showPasswordToggle = computed(() => props.passwordToggle && props.type === 'password' && !isDisabled.value)\nconst showClear = computed(() => resolvedClearable.value && props.modelValue.length > 0 && !isDisabled.value && !isReadonly.value)\n\nconst trailingCount = computed(() => (showClear.value ? 1 : 0) + (showPasswordToggle.value ? 1 : 0) + (props.loading ? 1 : 0))\nconst trailingReserve = computed(() => (trailingCount.value > 0 ? `${trailingCount.value * 28}px` : '0px'))\n\nconst {\n prefixEl,\n suffixEl,\n prefixStyle,\n suffixStyle,\n fieldPadding: inputStyle,\n} = useControlAddons(() => props, {\n defaultMinWidth: () => ADDON_MIN_WIDTH_BY_SIZE[resolvedSize.value],\n paddingX: () => paddingX[resolvedSize.value],\n trailingReserve: () => trailingReserve.value,\n})\n\n// Border/ring/disabled — на оболочке (`focus-within`), размеры/выравнивание — на инпуте.\nconst shellClass = computed(() => grInputShellClass({\n state: props.state,\n invalid: isInvalid.value,\n disabled: isDisabled.value,\n}))\n\nconst className = computed(() => grInputFieldClass({\n size: resolvedSize.value,\n align: props.textAlign,\n}))\n\nfunction onInput(e: Event): void {\n emit('update:modelValue', (e.target as HTMLInputElement).value)\n}\n\n// Объявленный emit уходит из `$attrs`, поэтому нативные события переизлучаем\n// руками — иначе `@change`/`@focus`/`@blur` у потребителя перестали бы работать.\nfunction onChange(e: Event): void {\n emit('change', (e.target as HTMLInputElement).value)\n}\n\nfunction onFocus(e: FocusEvent): void {\n emit('focus', e)\n}\n\nfunction onBlur(e: FocusEvent): void {\n emit('blur', e)\n}\n\n// ————— Trailing-контролы: очистка, переключатель пароля, счётчик символов.\nconst { t } = useGranularityTranslations()\nconst resolvedClearLabel = computed(() => props.clearLabel ?? t('gr.input.clear', 'Clear'))\nconst resolvedPasswordShowLabel = computed(() => props.passwordShowLabel ?? t('gr.input.showPassword', 'Show password'))\nconst resolvedPasswordHideLabel = computed(() => props.passwordHideLabel ?? t('gr.input.hidePassword', 'Hide password'))\n\n// Счётчик символов: `len` или `len / maxlength`.\nconst countText = computed(() =>\n props.maxlength !== undefined ? `${props.modelValue.length} / ${props.maxlength}` : String(props.modelValue.length),\n)\n\n/**\n * Живой регион объявляет только исчерпание лимита. Читать вслух каждый символ\n * нельзя — диктор захлебнётся, а сам счётчик и так связан с полем через\n * `aria-describedby` и читается при фокусе.\n */\nconst limitReached = computed(() =>\n props.maxlength !== undefined && props.modelValue.length >= props.maxlength,\n)\nconst liveMessage = computed(() =>\n limitReached.value ? t('gr.input.limitReached', 'Character limit reached') : '',\n)\n\nfunction clear(): void {\n emit('update:modelValue', '')\n emit('clear')\n inputEl.value?.focus()\n}\n\nfunction togglePassword(): void {\n passwordVisible.value = !passwordVisible.value\n inputEl.value?.focus()\n}\n</script>\n\n<template>\n <div data-gr-input class=\"w-full\">\n <div :class=\"shellClass\">\n <div\n v-if=\"$slots.prefix\"\n ref=\"prefixEl\"\n data-testid=\"gr-input-prefix\"\n class=\"absolute inset-y-0 left-0 flex items-center justify-center border-r border-[var(--gr-brd)] px-2 text-[var(--gr-muted-fg)] pointer-events-none select-none truncate\"\n :style=\"prefixStyle\"\n aria-hidden=\"true\"\n >\n <slot name=\"prefix\" />\n </div>\n\n <input\n :id=\"resolvedId\"\n ref=\"inputEl\"\n v-bind=\"$attrs\"\n :name=\"props.name\"\n :type=\"resolvedType\"\n :inputmode=\"props.inputmode\"\n :autocomplete=\"props.autocomplete\"\n :placeholder=\"props.placeholder\"\n :disabled=\"isDisabled\"\n :readonly=\"isReadonly\"\n :maxlength=\"props.maxlength\"\n :value=\"props.modelValue\"\n :aria-invalid=\"isInvalid ? 'true' : undefined\"\n :aria-describedby=\"describedBy\"\n :aria-required=\"isRequired ? 'true' : undefined\"\n :aria-readonly=\"isReadonly ? 'true' : undefined\"\n :aria-label=\"ariaLabel\"\n :aria-busy=\"props.loading ? 'true' : undefined\"\n class=\"w-full bg-transparent text-[var(--gr-fg)] placeholder:text-[var(--gr-muted-fg)] focus:placeholder:text-transparent focus:outline-none disabled:cursor-not-allowed disabled:text-[var(--gr-muted-fg)]\"\n :class=\"className\"\n :style=\"inputStyle\"\n @input=\"onInput\"\n @change=\"onChange\"\n @focus=\"onFocus\"\n @blur=\"onBlur\"\n >\n\n <div\n v-if=\"trailingCount > 0\"\n data-gr-input-trailing\n class=\"absolute inset-y-0 right-1 flex items-center gap-0.5\"\n >\n <span\n v-if=\"loading\"\n data-gr-input-spinner\n class=\"flex h-6 w-6 items-center justify-center text-[var(--gr-muted-fg)]\"\n aria-hidden=\"true\"\n >\n <IconLoader class=\"h-4 w-4 animate-spin\" />\n </span>\n\n <button\n v-if=\"showClear\"\n type=\"button\"\n data-gr-input-clear\n :aria-label=\"resolvedClearLabel\"\n class=\"flex h-6 w-6 items-center justify-center rounded-[var(--gr-radius-sm)] text-[var(--gr-muted-fg)] transition-colors hover:bg-[var(--gr-muted)] hover:text-[var(--gr-fg)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--gr-ring)]\"\n @click=\"clear\"\n >\n <IconX class=\"h-4 w-4\" aria-hidden=\"true\" />\n </button>\n\n <button\n v-if=\"showPasswordToggle\"\n type=\"button\"\n data-gr-input-password-toggle\n :aria-label=\"passwordVisible ? resolvedPasswordHideLabel : resolvedPasswordShowLabel\"\n :aria-pressed=\"passwordVisible ? 'true' : 'false'\"\n class=\"flex h-6 w-6 items-center justify-center rounded-[var(--gr-radius-sm)] text-[var(--gr-muted-fg)] transition-colors hover:bg-[var(--gr-muted)] hover:text-[var(--gr-fg)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--gr-ring)]\"\n @click=\"togglePassword\"\n >\n <IconEyeOff v-if=\"passwordVisible\" class=\"h-4 w-4\" aria-hidden=\"true\" />\n <IconEye v-else class=\"h-4 w-4\" aria-hidden=\"true\" />\n </button>\n </div>\n\n <div\n v-if=\"$slots.suffix\"\n ref=\"suffixEl\"\n data-testid=\"gr-input-suffix\"\n class=\"absolute inset-y-0 right-0 flex items-center justify-center border-l border-[var(--gr-brd)] px-2 text-[var(--gr-muted-fg)] pointer-events-none select-none truncate\"\n :class=\"suffixFixed ? '[direction:rtl]' : ''\"\n :style=\"suffixStyle\"\n aria-hidden=\"true\"\n >\n <slot name=\"suffix\" />\n </div>\n </div>\n\n <div\n v-if=\"showCount\"\n :id=\"countId\"\n data-gr-input-count\n class=\"mt-1 text-right text-[length:var(--gr-control-text-xs)] leading-[var(--gr-leading-xs)] text-[var(--gr-muted-fg)] tabular-nums\"\n >\n {{ countText }}\n </div>\n\n <span\n v-if=\"showCount\"\n data-gr-input-live\n class=\"sr-only\"\n role=\"status\"\n aria-live=\"polite\"\n >{{ liveMessage }}</span>\n </div>\n</template>\n"],"mappings":"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"}
|