Files
crm/apps/web/src/components/ui/UiField.vue
T
s.esmaeili 1ae87c97b7 feat(ui): standardize design system, modals, sidebar, and form inputs
- Implement unified design system tokens, 10px radius, and IRANSans typography
- Modernize sidebar with Gemini-style collapsed expander, square 1:1 items, and elevated profile flyout
- Standardize all modal dialogs with 16px padding, fixed header/footer, 16px medium title, and sticky bottom actions
- Refactor DealFormView and CustomFieldsSection with standard UiField, UiInput, and UiSelect components
- Add draggable bottom-left QuickCreate floating action button
2026-09-12 01:33:07 +03:30

105 lines
1.9 KiB
Vue

<script setup lang="ts">
withDefaults(
defineProps<{
label?: string;
forId?: string;
required?: boolean;
hint?: string;
error?: string | boolean;
horizontal?: boolean;
}>(),
{
required: false,
horizontal: false,
},
);
</script>
<template>
<div class="ui-field" :class="{ horizontal, hasError: !!error }">
<div v-if="label || $slots.label" class="label-row">
<label v-if="label" :for="forId" class="field-label">
{{ label }}
<span v-if="required" class="required-mark" aria-hidden="true">*</span>
</label>
<slot v-else name="label" />
<slot name="label-extra" />
</div>
<div class="control-wrap">
<slot />
<p v-if="typeof error === 'string' && error" class="field-error" role="alert">
{{ error }}
</p>
<p v-else-if="hint || $slots.hint" class="field-hint">
<slot name="hint">{{ hint }}</slot>
</p>
</div>
</div>
</template>
<style scoped>
.ui-field {
display: flex;
flex-direction: column;
gap: var(--sp-1);
min-width: 0;
width: 100%;
}
.ui-field.horizontal {
flex-direction: row;
align-items: center;
gap: var(--sp-3);
}
.ui-field.horizontal .label-row {
flex: 0 0 140px;
}
.ui-field.horizontal .control-wrap {
flex: 1;
}
.label-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--sp-2);
}
.field-label {
font-size: var(--fs-sm);
font-weight: var(--fw-semibold);
color: var(--ink-secondary);
line-height: var(--lh-tight);
}
.required-mark {
color: var(--danger);
margin-right: 2px;
}
.control-wrap {
display: flex;
flex-direction: column;
gap: 2px;
min-width: 0;
width: 100%;
}
.field-error {
margin: 2px 0 0;
font-size: var(--fs-xs);
color: var(--danger);
font-weight: var(--fw-medium);
}
.field-hint {
margin: 2px 0 0;
font-size: var(--fs-xs);
color: var(--muted);
line-height: var(--lh-normal);
}
</style>