Field
Базовый компонент текстового поля. Basic text input component.
Import Permalink to "Import"
import ProximaField from 'proxima-vue/field';
Playground Permalink to "Playground"
import { ref } from 'vue';
import ProximaField from 'proxima-vue/field';
const value = ref('');
<ProximaField
label="Username"
placeholder="Enter username"
v-model="value"
/>
Validity Permalink to "Validity"
Для демонстрации корректности заполнения поля можно использовать пропсы validityStyle
и validityStatus
, первый добавляет стили, второй иконку‑статус: To demonstrate the validity of a field, you can use the validityStyle
and validityStatus
props. The first adds styles, and the second adds a status icon:
<ProximaField
label="Minlength 1"
validity-style="both"
validity-status="both"
/>
<ProximaField
label="Minlength 6"
validity-style="both"
validity-status="both"
:minlength="6"
/>
Можно указать одно из значений both
, none
, valid
, invalid
, последнее используется по умолчанию, для демонстрации текстовых ошибок необходимо использовать компонент в связке с формой. You can specify one of the values: both
, none
, valid
or invalid
. The last of these is used by default. To demonstrate text errors, you must use the component with the form.
Icon Permalink to "Icon"
Специальный слот позволяет добавить свою иконку внутрь поля: A special slot allows you to add your own icon inside the field:
<ProximaField
placeholder="Type to search ..."
v-model="value"
>
<template #prepend>
<ProximaIconSearch />
</template>
</ProximaField>
import { ref } from 'vue';
import ProximaField from 'proxima-vue/field';
import ProximaIconSearch from 'proxima-vue/icon/search';
const value = ref('');
Actions Permalink to "Actions"
Вы можете добавить свои экшены через пропс actions
: You can add your own actions using the actions
prop:
<ProximaField
placeholder="Field with voice action"
actions-visibility="always"
:actions="fieldActions"
/>
import { ref, unref, computed } from 'vue';
import ProximaField from 'proxima-vue/field';
const isRecording = ref(false);
const toggleRecording = () => {
alert(unref(isRecording) ? 'stop recording' : 'start recording');
isRecording.value = !unref(isRecording);
};
const fieldActions = computed(() => [
{
id: 'recording',
pressed: unref(isRecording),
html: '<svg viewBox="0 0 16 16" width="16" height="16" aria-hidden="true" style="transform: scale(1.25);"><path fill="currentColor" d="M11 4v4c0 1.6-1.4 3-3 3S5 9.6 5 8V4c0-1.6 1.4-3 3-3s3 1.4 3 3Z"/><path fill="none" stroke-width="1" stroke="currentColor" d="M6 14.5h2v-2 2h2zM3.5 8c0 2.3 1.8 4.498 4.5 4.498S12.5 10.3 12.5 8"/></svg>',
onClick: toggleRecording,
},
]);
interface ProximaFieldAction {
id: string
html: string
shouldPreventBlur?: boolean
pressed?: boolean
onClick: Function
}
Skeleton Permalink to "Skeleton"
Режим плейсхолдера во время загрузки: Placeholder mode on loading:
Атрибут data‑ghost требует The attribute data‑ghost requires ghost.css
<div data-ghost="true" inert>
<ProximaField
label="Username"
/>
</div>
Accessibility Permalink to "Accessibility"
Компонент использует нативное поле input
, для описания используется label
, также можно добавить расширенное описание для вспомогательных технологий с помощью пропса describedby
: The component uses a native input
field, with a label
used for description. You can also add an extended description for assistive technologies using the describedby
prop:
<ProximaField
describedby="my-hidden-description"
/>
<p id="my-hidden-description" hidden>
The extended description for assistive technologies can be hidden
</p>
BEM Permalink to "BEM"
Блок компонента field
, модификаторы (создаются автоматически на основе пропсов): Component block field
, modifiers (automatically created based on props):
field_focused
field_enabled
field_disabled
field_invalid
field_valid
field_required
field_readonly
field_has_label
field_has_prepend
field_has_value
field_has_arrow
field_has_status
field_has_actions
field_label_above
field_label_inside
field_label_aside
field_actions_hover
field_actions_always
field_view_plain
field_view_line
field_size_xxs
field_size_xs
field_size_s
field_size_normal
field_size_m
field_size_l
field_size_xl
field_size_xxl
field_round_soft
field_round_full
field_round_none
field_shadow_none
field_shadow_soft
field_theme_[name]
CSS Permalink to "CSS"
Слой компонента @layer proxima.field
, также вы можете гибко cтилизовать компонент через кастомные свойства: Component layer @layer proxima.field
, also you can style the component flexibly through custom properties:
--field-accent-color: var(--app-accent-color);
--field-background: var(--app-field-background);
--field-autofill-color: #e8f0fe;
--field-mask-color: var(--field-placeholder-color);
--field-transition: background-color 200ms, border-color 200ms;
/* Placeholder */
--field-placeholder-color: #6c757d;
--field-placeholder-font-family: inherit;
--field-placeholder-font-size: inherit;
--field-placeholder-font-weight: inherit;
--field-placeholder-font-style: normal;
--field-placeholder-line-height: normal;
--field-placeholder-opacity: 1;
/* Value */
--field-color: var(--app-field-color);
--field-caret-color: auto;
--field-font-family: inherit;
--field-font-weight: 400;
--field-font-style: normal;
--field-text-transform: none;
--field-text-align: start;
--field-line-height: normal;
--field-text-overflow: ellipsis;
/* Padding */
--field-padding-y: 0;
--field-padding-x-start: calc(
(var(--field-size) * 0.25) +
(var(--field-is-full-round) * 0.375rem)
);
--field-padding-x-end: calc(
(var(--field-size) * 0.25) +
(var(--field-is-full-round) * 0.375rem)
);
/* Max width */
--field-width-length-multiplier: calc(var(--field-font-size) * 0.61);
--field-max-width: calc(
var(--field-padding-x-start) +
var(--field-padding-x-end) + (
var(--field-width-length) *
var(--field-width-length-multiplier)
)
);
/* Border */
--field-border-color: var(--app-field-border-color);
--field-border-width: 1px;
--field-border-style: solid;
/* Soft round */
--field-border-radius-divider: 16;
--field-border-radius: calc(
var(--field-size) / var(--field-border-radius-divider)
);
/* Shadow */
--field-box-shadow-soft-size: 0.0625rem 0.0625rem 0.25rem;
--field-box-shadow-soft-color: rgba(0, 0, 0, 0.1);
--field-view-line-box-shadow-soft-size: 0;
/* Header */
--field-header-margin-y-end: calc(
var(--field-label-font-size) * 0.375
);
/* Footer */
--field-footer-max-width: none;
--field-footer-margin-y-start: 0.5rem;
/* Label */
--field-label-letter-spacing: normal;
--field-label-line-height: 1.2;
--field-label-color: var(--app-label-color);
--field-label-required-display: inline;
--field-label-required-color: inherit;
--field-label-aside-header-justify-content: flex-start;
--field-label-aside-header-padding-x-end: 1rem;
--field-label-aside-width-multiplier: 10;
--field-label-aside-header-max-width: calc(
var(--field-label-font-size) *
var(--field-label-aside-width-multiplier)
);
--field-label-inside-padding-y-start: calc(var(--field-size) / 3.5);
--field-label-inside-label-transition: transform 200ms;
--field-label-inside-header-z-index: 5;
--field-label-inside-label-transform: scale(0.8) translateY(
calc((33% + (var(--field-size) / 10)) * -1))
);
/* Prepend */
--field-prepend-font-size: calc(var(--field-font-size) * 1.25);
--field-prepend-width: var(--field-size);
--field-prepend-height: 100%;
--field-prepend-background: none;
--field-prepend-color: var(--field-color);
--field-prepend-padding-x: 0;
--field-prepend-padding-y: 0;
--field-prepend-border-style: var(--field-border-style);
--field-prepend-border-width: 0;
--field-prepend-border-color: var(--field-border-color);
--field-prepend-pointer-events: none;
--field-prepend-z-index: 2;
/* Action */
--field-action-color: #787a7d;
--field-action-pressed-color: var(--field-accent-color);
--field-action-size: var(--field-size);
--field-action-padding: 0;
--field-action-icon-stroke-width: revert-layer;
--field-action-icon-stroke-linecap: round;
--field-action-icon-stroke-linejoin: round;
--field-action-sep-display: block;
--field-action-sep-style: dotted;
--field-action-sep-width: 1px;
--field-action-sep-color: rgba(107, 107, 107, 0.5);
--field-action-transition: color 200ms, opacity 300ms;
--field-actions-z-index: 2;
/* Arrow */
--field-arrow-pointer-events: none;
--field-arrow-transition:
transform 200ms cubic-bezier(.455, .03, .515, .955);
/* Status */
--field-status-size: var(--field-font-size);
--field-status-opacity: calc(1 - var(--field-is-hover-or-focus));
--field-status-background-display: block;
--field-status-background: linear-gradient(
calc(90deg * var(--field-direction)),
transparent,
var(--field-background) 50%
);
--field-status-icon-stroke-width: 2;
--field-status-icon-stroke-linecap: round;
--field-status-icon-stroke-linejoin: round;
--field-status-invalid-color: var(--field-invalid-color);
--field-status-valid-color: var(--field-valid-color);
--field-status-z-index: 3;
--field-status-transform: translate3d(0, calc(
var(--field-is-hover-or-focus) * -25%
), 0);
--field-status-transition:
opacity 250ms ease-out,
transform 250ms cubic-bezier(0, 0.55, 0.45, 1);
/* Invalid */
--field-invalid-color: var(--app-invalid-color);
--field-invalid-background: var(--field-background);
--field-invalid-border-color: var(--field-invalid-color);
--field-invalid-mask-color: color-mix(in srgb,
var(--field-invalid-color), #fff 50%
);
/* Valid */
--field-valid-color: var(--app-valid-color);
--field-valid-background: var(--field-background);
--field-valid-border-color: var(--field-valid-color);
/* View line */
--field-view-line-padding-x: 0rem;
--field-view-line-background: transparent;
--field-view-line-border-radius: 0;
--field-view-line-highlight-offset: 0.25rem;
/* Disabled */
--field-disabled-opacity: var(--app-disabled-opacity);
/* Hover */
--field-hover-background: var(--field-background);
--field-hover-border-color: var(--field-accent-color);
--field-action-hover-color: var(--field-color);
/* Focus */
--field-focus-background: var(--field-background);
--field-focus-border-color: var(--field-accent-color);
--field-focus-placeholder-color: color-mix(in srgb,
var(--field-placeholder-color), transparent 40%
);
/* Focus highlight */
--field-highlight-offset: var(--app-highlight-offset);
--field-highlight-style: var(--app-highlight-style);
--field-highlight-size: var(--app-highlight-size);
--field-highlight-color: var(--app-highlight-color);
/*
Sizes
*/
--field-size-xxs: 1.875rem;
--field-font-size-xxs: 0.8125rem;
--field-label-font-size-xxs: 0.75rem;
--field-action-icon-size-xxs: 0.75rem;
--field-size-xs: 2.25rem;
--field-font-size-xs: 0.875rem;
--field-label-font-size-xs: 0.8125rem;
--field-action-icon-size-xs: 0.75rem;
--field-size-s: 2.5rem;
--field-font-size-s: 1rem;
--field-label-font-size-s: 0.875rem;
--field-action-icon-size-s: 0.875rem;
/* Normal */
--field-size: 2.75rem;
--field-font-size: 1rem;
--field-label-font-size: 0.875rem;
--field-action-icon-size: 0.875rem;
--field-size-m: 3rem;
--field-font-size-m: 1rem;
--field-label-font-size-m: 0.875rem;
--field-action-icon-size-m: 0.875rem;
--field-size-l: 3.375rem;
--field-font-size-l: 1.125rem;
--field-label-font-size-l: 1rem;
--field-action-icon-size-l: 1rem;
--field-size-xl: 3.75rem;
--field-font-size-xl: 1.25rem;
--field-label-font-size-xl: 1.125rem;
--field-action-icon-size-xl: 1.125rem;
--field-size-xxl: 4.125rem;
--field-font-size-xxl: 1.375rem;
--field-label-font-size-xxl: 1.25rem;
--field-action-icon-size-xxl: 1.125rem;
/*
Flags (0 or 1, based on props)
*/
--field-direction: 1; /* or -1 on rtl */
--field-has-prepend: 0;
--field-has-actions: 0;
--field-has-label: 0;
--field-has-value: 0;
--field-has-status: 0;
--field-has-arrow: 0;
--field-is-valid: 0;
--field-is-invalid: 0;
--field-is-enabled: 0;
--field-is-disabled: 0;
--field-is-label-above: 0;
--field-is-label-aside: 0;
--field-is-label-inside: 0;
--field-is-actions-hover: 0;
--field-is-actions-always: 0;
--field-is-full-round: 0;
--field-is-arrow-flipped: 0;
--field-is-view-plain: 0;
--field-is-view-line: 0;
--field-is-hover: 0;
--field-is-focus: 0;
--field-is-hover-or-focus: max(
var(--field-is-focus, 0),
var(--field-is-hover, 0)
);
Locale Permalink to "Locale"
Компонент использует токены локализации: The component uses localization tokens:
{
fieldInvalid: "The field is not valid",
fieldRequired: "The field is required",
fieldBadLength: "Minimum number of characters: {minlength}, currently: {count}",
}
Props Permalink to "Props"
type ProximaFieldModelModifiers = {
lazy?: boolean
trim?: boolean
number?: boolean
}
interface ProximaFieldProps {
id?: string
inputAttrs?: InputHTMLAttributes
modelModifiers?: ProximaFieldModelModifiers
modelValue?: string | number
label?: string
labelPosition?: 'above' | 'inside' | 'aside'
describedby?: string
autocomplete?: string
placeholder?: string
disabled?: boolean
readonly?: boolean
required?: boolean
autofocus?: boolean
minlength?: number
maxlength?: number
validityStyle?: 'none' | 'valid' | 'invalid' | 'both'
validityStatus?: 'none' | 'valid' | 'invalid' | 'both'
errorMessage?: string
actions?: ProximaFieldAction[]
actionsVisibility?: 'hover' | 'always'
hasClearButton?: boolean
hasArrowButton?: boolean
width?: string
validator?: (value: string, props?: ProximaFieldProps) => boolean
emptyChecker?: (value: string) => boolean
parseValue?: (value: any, currentValue: string) => string
view?: 'plain' | 'line'
size?: ProximaSize
round?: 'soft' | 'full' | 'none'
shadow?: 'soft' | 'none'
theme?: string
}
Events Permalink to "Events"
<ProximaField
@click:arrow="onFieldArrowClick"
@update:modelValue="onFieldValueUpdate"
@complete="onFieldComplete"
@change="onFieldChange"
@clear="onFieldClear"
@focus="onFieldFocus"
@blur="onFieldBlur"
/>
const onFieldArrowClick = () => {};
const onFieldValueUpdate = (modelValue) => {};
const onFieldComplete = (modelValue) => {};
const onFieldChange = (modelValue) => {};
const onFieldClear = (clearedValue) => {};
const onFieldFocus = (event) => {};
const onFieldBlur = (event) => {};
type Emits = {
'click:arrow': () => void
'update:modelValue': (modelValue: string | number) => void
'complete': (modelValue: string | number) => void
'change': (modelValue: string | number) => void
'clear': (clearedValue: string) => void
'focus': (event: FocusEvent) => void
'blur': (event: FocusEvent) => void
}
Slots Permalink to "Slots"
<ProximaField>
<template #label="slotProps">
you code (replaced label)
</template>
<template #header="slotProps">
you code (append to header)
</template>
<template #prepend="slotProps">
you code (append to prepend)
</template>
<template #field="slotProps">
you code (replaced field)
</template>
<template #status-valid="slotProps">
you code (replaced valid status)
</template>
<template #status-invalid="slotProps">
you code (replaced invalid status)
</template>
<template #content="slotProps">
you code (append to content)
</template>
<template #footer="slotProps">
you code (append to footer)
</template>
</ProximaField>
type SlotProps = {
id: string
label: string
placeholder: string
fieldValue: string
fieldAttrs: InputHTMLAttributes
isFocused: boolean
isCorrectLength: boolean
isEmpty: boolean
isValid: boolean
hasLabel: boolean
hasHeader: boolean
hasFooter: boolean
hasPrepend: boolean
hasActions: boolean
hasValidStyle: boolean
hasInvalidStyle: boolean
hasValidStatus: boolean
hasInvalidStatus: boolean
updateByEvent: () => void
onBlur: () => void
onFocus: () => void
select: () => void
clear: () => void
focus: () => void
blur: () => void
}
Expose Permalink to "Expose"
<ProximaField ref="field" />
import { ref, unref, onMounted } from 'vue';
import ProximaField from 'proxima-vue/field';
const field = ref({} as InstanceType<typeof ProximaField>);
onMounted(() => {
unref(field).checkFocus(); // false
unref(field).focus();
unref(field).checkFocus(); // true
});
type ProximaFieldInstance = {
getContainer: () => HTMLDivElement | null
getElement: () => HTMLInputElement | null
getLengthRange: () => [number, number]
getValue: () => string
getError: () => string
getId: () => string
checkEmpty: () => boolean
checkFocus: () => boolean
checkLength: () => boolean
checkValidity: () => boolean
select: () => void
clear: () => void
focus: () => void
blur: () => void
}