plugin-ui-for-kzt 0.0.21 → 0.0.23
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/components/BaseBadge/BaseBadge.vue.d.ts +1 -1
- package/dist/components/BaseButton/BaseButton.vue.d.ts +1 -3
- package/dist/components/BaseCalendar/BaseCalendar.vue.d.ts +9 -1
- package/dist/components/BaseCheckbox/BaseCheckbox.vue.d.ts +1 -3
- package/dist/components/BaseDropdown/BaseDropdown.vue.d.ts +1 -3
- package/dist/components/BaseField/BaseField.vue.d.ts +98 -0
- package/dist/components/BaseInput/BaseInput.vue.d.ts +19 -17
- package/dist/components/BaseInputCalendar/BaseInputCalendar.vue.d.ts +15 -17
- package/dist/components/BaseInputCurrency/BaseInputCurrency.vue.d.ts +10 -12
- package/dist/components/BaseInputEmail/BaseInputEmail.vue.d.ts +9 -14
- package/dist/components/BaseInputPhone/BaseInputPhone.vue.d.ts +9 -14
- package/dist/components/BaseOpenedListItem/BaseOpenedListItem.vue.d.ts +1 -3
- package/dist/components/BasePagination/BasePagination.vue.d.ts +1 -1
- package/dist/components/BaseRadio/BaseRadio.vue.d.ts +1 -3
- package/dist/components/BaseSegmentedButtons/BaseSegmentedButtons.vue.d.ts +1 -3
- package/dist/components/BaseSelect/BaseSelect.vue.d.ts +1 -1
- package/dist/components/BaseSiteInput/BaseSiteInput.vue.d.ts +11 -7
- package/dist/components/BaseTabs/BaseTabs.vue.d.ts +25 -0
- package/dist/components/BaseTag/BaseTag.vue.d.ts +1 -1
- package/dist/components/BaseTextarea/BaseTextarea.vue.d.ts +9 -14
- package/dist/components/BaseToggle/BaseToggle.vue.d.ts +1 -3
- package/dist/components/BaseTooltip/BaseTooltip.vue.d.ts +1 -1
- package/dist/components/BaseUpload/BaseUpload.vue.d.ts +11 -0
- package/dist/components/BaseUpload/CropModal.vue.d.ts +9 -0
- package/dist/composables/kit/state.d.ts +1 -2
- package/dist/index.d.ts +3 -1
- package/dist/index.js +1 -1
- package/dist/index.js.LICENSE.txt +15 -0
- package/example/App.vue +37 -31
- package/example/TestImage.vue +6 -0
- package/package.json +2 -1
- package/src/components/BaseCheckbox/BaseCheckbox.vue +76 -46
- package/src/components/BaseField/BaseField.vue +184 -0
- package/src/components/BaseField/README.md +85 -0
- package/src/components/BaseInput/BaseInput.vue +162 -228
- package/src/components/BaseInputCalendar/BaseInputCalendar.vue +10 -7
- package/src/components/BaseInputCurrency/BaseInputCurrency.vue +39 -78
- package/src/components/BaseInputEmail/BaseInputEmail.vue +2 -4
- package/src/components/BaseInputPhone/BaseInputPhone.vue +29 -89
- package/src/components/BaseRadio/BaseRadio.vue +266 -233
- package/src/components/BaseSelect/BaseSelect.vue +9 -52
- package/src/components/BaseSiteInput/BaseSiteInput.vue +11 -63
- package/src/components/BaseTabs/BaseTabs.vue +193 -0
- package/src/components/BaseTextarea/BaseTextarea.vue +3 -30
- package/src/components/BaseUpload/BaseUpload.vue +35 -1
- package/src/components/BaseUpload/CropModal.vue +210 -0
- package/src/composables/kit/state.ts +1 -2
- package/src/index.ts +8 -2
- package/src/styles/index.scss +87 -2
- package/src/styles/root.scss +1 -0
- package/src/styles/variables.scss +2 -1
- package/src/types/calendar.d.ts +2 -0
- package/src/types/field.d.ts +12 -0
- package/src/types/input.d.ts +26 -8
- package/src/types/tab.d.ts +17 -0
- package/src/types/uploadedFile.d.ts +7 -0
- package/src/types/utils.d.ts +0 -1
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="base-select" :class="[classList]">
|
|
3
3
|
<div class="base-select__wrapper">
|
|
4
|
-
<label v-if="label" :for="id" class="base-select__label">{{ label }}</label>
|
|
5
|
-
|
|
6
4
|
<base-dropdown
|
|
7
5
|
v-model="dropdownVisible"
|
|
8
6
|
transition-name="top"
|
|
@@ -17,7 +15,7 @@
|
|
|
17
15
|
>
|
|
18
16
|
<slot
|
|
19
17
|
name="header"
|
|
20
|
-
:value="actualOption"
|
|
18
|
+
:value="actualOption ? [actualOption] : undefined"
|
|
21
19
|
>
|
|
22
20
|
<div v-if="$slots.headerIcon" class="base-select__header_prefix_icon">
|
|
23
21
|
<slot name="headerIcon" />
|
|
@@ -63,7 +61,7 @@
|
|
|
63
61
|
key-field="id"
|
|
64
62
|
class="base-select__list"
|
|
65
63
|
>
|
|
66
|
-
<template #default="{ item, index, active } :
|
|
64
|
+
<template #default="{ item, index, active } : ISelectSlotProps">
|
|
67
65
|
<dynamic-scroller-item
|
|
68
66
|
:item="item"
|
|
69
67
|
:active="active"
|
|
@@ -90,10 +88,6 @@
|
|
|
90
88
|
</div>
|
|
91
89
|
</template>
|
|
92
90
|
</base-dropdown>
|
|
93
|
-
|
|
94
|
-
<div v-if="(error && typeof error === 'string') || hint" class="base-select__hint">
|
|
95
|
-
{{ error || hint }}
|
|
96
|
-
</div>
|
|
97
91
|
</div>
|
|
98
92
|
</div>
|
|
99
93
|
</template>
|
|
@@ -102,20 +96,13 @@
|
|
|
102
96
|
import { computed, ref, watch } from 'vue';
|
|
103
97
|
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css';
|
|
104
98
|
import { DynamicScroller, DynamicScrollerItem } from 'vue-virtual-scroller';
|
|
105
|
-
import type { ICoreSelectProps, TSelectValue, ICoreSelectBaseProps } from '../../types/input';
|
|
99
|
+
import type { ICoreSelectProps, TSelectValue, ICoreSelectBaseProps, ICoreSelectOption, ISelectSlotProps } from '../../types/input';
|
|
106
100
|
import BaseDropdown from '../BaseDropdown/BaseDropdown.vue';
|
|
107
101
|
import BaseOpenedListItem from '../BaseOpenedListItem/BaseOpenedListItem.vue';
|
|
108
102
|
import { useKitSize } from '../../composables/kit/size';
|
|
109
103
|
import { useKitState } from '../../composables/kit/state';
|
|
110
104
|
import { useKitStyle } from '../../composables/kit/style';
|
|
111
105
|
|
|
112
|
-
// Определяем тип для slotProps
|
|
113
|
-
interface SlotProps {
|
|
114
|
-
item: ICoreSelectBaseProps;
|
|
115
|
-
index: number;
|
|
116
|
-
active: boolean;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
106
|
const props = withDefaults(defineProps<ICoreSelectProps & {
|
|
120
107
|
modelValue?: TSelectValue
|
|
121
108
|
}>(), {
|
|
@@ -130,11 +117,11 @@ const emit = defineEmits<{
|
|
|
130
117
|
|
|
131
118
|
const actualValue = ref<TSelectValue>(props.modelValue ?? '');
|
|
132
119
|
const actualOption = computed(() =>
|
|
133
|
-
props.options?.find((item:
|
|
120
|
+
props.options?.find((item: ICoreSelectOption) => item?.id === actualValue.value) || null
|
|
134
121
|
);
|
|
135
122
|
|
|
136
123
|
watch(() => props.modelValue, (val) => {
|
|
137
|
-
actualValue.value = val;
|
|
124
|
+
actualValue.value = val ?? '';
|
|
138
125
|
}, { immediate: true });
|
|
139
126
|
|
|
140
127
|
function handleInput(value: TSelectValue) {
|
|
@@ -152,7 +139,7 @@ const { styleClassList } = useKitStyle(props);
|
|
|
152
139
|
const listItemAttrs = computed(() => (item: ICoreSelectBaseProps) => ({
|
|
153
140
|
icon: item.icon,
|
|
154
141
|
active: actualValue.value === item.id,
|
|
155
|
-
title: item.name ||
|
|
142
|
+
title: item.name || '',
|
|
156
143
|
style: item.style,
|
|
157
144
|
disabled: item.disabled,
|
|
158
145
|
class: 'base-select__item',
|
|
@@ -170,8 +157,10 @@ const classList = computed(() => [
|
|
|
170
157
|
]);
|
|
171
158
|
|
|
172
159
|
defineSlots<{
|
|
173
|
-
default(props:
|
|
160
|
+
default(props: ISelectSlotProps): any;
|
|
174
161
|
iconItem(props: { item: ICoreSelectBaseProps }): any;
|
|
162
|
+
header(props: { value: ICoreSelectProps['options'] }): any;
|
|
163
|
+
headerIcon(): any;
|
|
175
164
|
}>();
|
|
176
165
|
</script>
|
|
177
166
|
|
|
@@ -205,14 +194,6 @@ defineSlots<{
|
|
|
205
194
|
}
|
|
206
195
|
|
|
207
196
|
&.--is-error {
|
|
208
|
-
#{$select}__hint {
|
|
209
|
-
color: var(--error-red);
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
#{$select}__label {
|
|
213
|
-
border-color: var(--error-red-light-01);
|
|
214
|
-
}
|
|
215
|
-
|
|
216
197
|
#{$select}__arrow > * {
|
|
217
198
|
color: var(--error-red);
|
|
218
199
|
}
|
|
@@ -276,14 +257,6 @@ defineSlots<{
|
|
|
276
257
|
|
|
277
258
|
&.--small-size {
|
|
278
259
|
#{$select} {
|
|
279
|
-
&__label {
|
|
280
|
-
font: var(--typography-text-s-medium);
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
&__hint {
|
|
284
|
-
font: var(--typography-text-s-regular);
|
|
285
|
-
}
|
|
286
|
-
|
|
287
260
|
&__header_value {
|
|
288
261
|
font: var(--typography-text-m-regular);
|
|
289
262
|
}
|
|
@@ -307,14 +280,6 @@ defineSlots<{
|
|
|
307
280
|
|
|
308
281
|
&.--medium-size {
|
|
309
282
|
#{$select} {
|
|
310
|
-
&__label {
|
|
311
|
-
font: var(--typography-text-s-medium);
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
&__hint {
|
|
315
|
-
font: var(--typography-text-s-regular);
|
|
316
|
-
}
|
|
317
|
-
|
|
318
283
|
&__header_value {
|
|
319
284
|
font: var(--typography-text-m-regular);
|
|
320
285
|
}
|
|
@@ -333,14 +298,6 @@ defineSlots<{
|
|
|
333
298
|
|
|
334
299
|
&.--large-size {
|
|
335
300
|
#{$select} {
|
|
336
|
-
&__label {
|
|
337
|
-
font: var(--typography-text-m-medium);
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
&__hint {
|
|
341
|
-
font: var(--typography-text-m-regular);
|
|
342
|
-
}
|
|
343
|
-
|
|
344
301
|
&__header_value {
|
|
345
302
|
font: var(--typography-text-l-regular);
|
|
346
303
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div :class="classList">
|
|
3
3
|
<div class="base-site-input__wrapper">
|
|
4
|
-
<div
|
|
4
|
+
<div class="base-site-input__url-wrapper">
|
|
5
5
|
<span class="base-site-input__url">
|
|
6
6
|
https://
|
|
7
7
|
</span>
|
|
@@ -9,9 +9,10 @@
|
|
|
9
9
|
<base-input
|
|
10
10
|
v-bind="{...$props, ...$attrs}"
|
|
11
11
|
v-model="modelValue"
|
|
12
|
-
id="
|
|
12
|
+
:id="id"
|
|
13
13
|
type="url"
|
|
14
14
|
:error="urlError || props.error"
|
|
15
|
+
class="base-site-input__input"
|
|
15
16
|
@update:modelValue="onUpdateModelValue"
|
|
16
17
|
/>
|
|
17
18
|
</div>
|
|
@@ -22,30 +23,24 @@
|
|
|
22
23
|
import { computed, ref, watch } from 'vue';
|
|
23
24
|
import BaseInput from '../BaseInput/BaseInput.vue';
|
|
24
25
|
import { useKitSize } from '../../composables/kit/size'
|
|
25
|
-
import { useKitState } from '../../composables/kit/state';
|
|
26
26
|
import type { ICorePropsBaseSiteInput } from '../../types/input'
|
|
27
27
|
|
|
28
28
|
const props = withDefaults(defineProps<ICorePropsBaseSiteInput>(),{
|
|
29
29
|
size: 'medium',
|
|
30
30
|
modelValue: '',
|
|
31
|
-
|
|
31
|
+
focusable: true,
|
|
32
32
|
})
|
|
33
33
|
|
|
34
34
|
const emit = defineEmits<{
|
|
35
35
|
(event: 'update:modelValue', value: string): void;
|
|
36
36
|
}>();
|
|
37
37
|
|
|
38
|
-
const urlError = ref('');
|
|
38
|
+
const urlError = ref<string>('');
|
|
39
39
|
const { sizeClassList } = useKitSize(props);
|
|
40
|
-
const { stateClassList } = useKitState(props);
|
|
41
40
|
|
|
42
41
|
const classList = computed(() => [
|
|
43
42
|
sizeClassList.value,
|
|
44
|
-
|
|
45
|
-
'base-site-input',
|
|
46
|
-
{
|
|
47
|
-
'--is-has-hint': !!props.hint,
|
|
48
|
-
}
|
|
43
|
+
'base-site-input'
|
|
49
44
|
]);
|
|
50
45
|
|
|
51
46
|
const normalizedValue = computed(() => {
|
|
@@ -82,7 +77,7 @@ watch(modelValue, (value) => {
|
|
|
82
77
|
.base-site-input {
|
|
83
78
|
$input: &;
|
|
84
79
|
|
|
85
|
-
width:
|
|
80
|
+
width: 100%;
|
|
86
81
|
|
|
87
82
|
&__wrapper {
|
|
88
83
|
display: flex;
|
|
@@ -97,6 +92,10 @@ watch(modelValue, (value) => {
|
|
|
97
92
|
}
|
|
98
93
|
}
|
|
99
94
|
|
|
95
|
+
&__input {
|
|
96
|
+
flex-grow: 1;
|
|
97
|
+
}
|
|
98
|
+
|
|
100
99
|
&__url {
|
|
101
100
|
color: var(--primary-text-tertiary);
|
|
102
101
|
}
|
|
@@ -114,99 +113,48 @@ watch(modelValue, (value) => {
|
|
|
114
113
|
}
|
|
115
114
|
|
|
116
115
|
&.--small-size {
|
|
117
|
-
&.--is-has-hint, &.--is-error {
|
|
118
|
-
#{$input} {
|
|
119
|
-
&__url-wrapper {
|
|
120
|
-
margin-bottom: 26px;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
116
|
#{$input} {
|
|
126
117
|
&__url-wrapper{
|
|
127
118
|
height: 40px;
|
|
128
119
|
font: var(--typography-text-m-regular);
|
|
129
120
|
border-top-left-radius: var(--corner-radius-s);
|
|
130
121
|
border-bottom-left-radius: var(--corner-radius-s);
|
|
131
|
-
|
|
132
|
-
&.--is-error {
|
|
133
|
-
margin-bottom: 26px;
|
|
134
|
-
}
|
|
135
122
|
}
|
|
136
123
|
|
|
137
124
|
&__url {
|
|
138
125
|
padding: var(--spacing-s) var(--spacing-m) var(--spacing-s) var(--spacing-2l);
|
|
139
126
|
}
|
|
140
127
|
}
|
|
141
|
-
|
|
142
|
-
:deep(.base-input__label-text),
|
|
143
|
-
:deep(.base-input__hint) {
|
|
144
|
-
transform: translateX(-75px);
|
|
145
|
-
}
|
|
146
128
|
}
|
|
147
129
|
|
|
148
130
|
&.--medium-size {
|
|
149
|
-
&.--is-has-hint, &.--is-error {
|
|
150
|
-
#{$input} {
|
|
151
|
-
&__url-wrapper {
|
|
152
|
-
margin-bottom: 26px;
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
131
|
#{$input} {
|
|
158
132
|
&__url-wrapper{
|
|
159
133
|
height: 48px;
|
|
160
134
|
font: var(--typography-text-m-regular);
|
|
161
135
|
border-top-left-radius: var(--corner-radius-m);
|
|
162
136
|
border-bottom-left-radius: var(--corner-radius-m);
|
|
163
|
-
|
|
164
|
-
&.--is-error {
|
|
165
|
-
margin-bottom: 26px;
|
|
166
|
-
}
|
|
167
137
|
}
|
|
168
138
|
|
|
169
139
|
&__url {
|
|
170
140
|
padding: var(--spacing-m) var(--spacing-m) var(--spacing-m) var(--spacing-2l);
|
|
171
141
|
}
|
|
172
142
|
}
|
|
173
|
-
|
|
174
|
-
:deep(.base-input__label-text),
|
|
175
|
-
:deep(.base-input__hint) {
|
|
176
|
-
transform: translateX(-75px);
|
|
177
|
-
}
|
|
178
143
|
}
|
|
179
144
|
|
|
180
145
|
&.--large-size {
|
|
181
|
-
&.--is-has-hint, &.--is-error {
|
|
182
|
-
#{$input} {
|
|
183
|
-
&__url-wrapper {
|
|
184
|
-
margin-bottom: 30px;
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
146
|
#{$input} {
|
|
190
147
|
&__url-wrapper{
|
|
191
148
|
height: 56px;
|
|
192
149
|
font: var(--typography-text-l-regular);
|
|
193
150
|
border-top-left-radius: var(--corner-radius-m);
|
|
194
151
|
border-bottom-left-radius: var(--corner-radius-m);
|
|
195
|
-
|
|
196
|
-
&.--is-error {
|
|
197
|
-
margin-bottom: 30px;
|
|
198
|
-
}
|
|
199
152
|
}
|
|
200
153
|
|
|
201
154
|
&__url {
|
|
202
155
|
padding: var(--spacing-2l) var(--spacing-m) var(--spacing-2l) var(--spacing-l);
|
|
203
156
|
}
|
|
204
157
|
}
|
|
205
|
-
|
|
206
|
-
:deep(.base-input__label-text),
|
|
207
|
-
:deep(.base-input__hint) {
|
|
208
|
-
transform: translateX(-83px);
|
|
209
|
-
}
|
|
210
158
|
}
|
|
211
159
|
}
|
|
212
160
|
</style>
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="base-tabs" :class="classList">
|
|
3
|
+
<div class="base-tabs__header">
|
|
4
|
+
<base-button
|
|
5
|
+
v-for="tab in tabs"
|
|
6
|
+
:key="tab.id"
|
|
7
|
+
size="custom"
|
|
8
|
+
color="custom"
|
|
9
|
+
class="base-tabs__tab"
|
|
10
|
+
:class="{ 'base-tabs__tab--active': activeTab === tab.id }"
|
|
11
|
+
@click="selectTab(tab.id)"
|
|
12
|
+
:disabled="tab.disabled"
|
|
13
|
+
>
|
|
14
|
+
<div v-if="tab.icon" class="base-tab__icon">
|
|
15
|
+
<component :is="tab.icon" />
|
|
16
|
+
</div>
|
|
17
|
+
<span>{{ tab.title }}</span>
|
|
18
|
+
</base-button>
|
|
19
|
+
</div>
|
|
20
|
+
<div class="base-tabs__content">
|
|
21
|
+
<slot />
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
</template>
|
|
25
|
+
|
|
26
|
+
<script setup lang="ts">
|
|
27
|
+
import { ref, computed, watch } from 'vue';
|
|
28
|
+
import type { TTabProps } from '../../types/tab.d';
|
|
29
|
+
import BaseButton from '../BaseButton/BaseButton.vue';
|
|
30
|
+
|
|
31
|
+
const props = withDefaults(defineProps<TTabProps>(), {
|
|
32
|
+
size: 'medium',
|
|
33
|
+
modelValue: 0,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const emit = defineEmits(['update:modelValue']);
|
|
37
|
+
|
|
38
|
+
const activeTab = ref(props.modelValue);
|
|
39
|
+
|
|
40
|
+
watch(() => props.modelValue, (newValue) => {
|
|
41
|
+
activeTab.value = newValue;
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const selectTab = (id: number) => {
|
|
45
|
+
if (!props.tabs[id]?.disabled) {
|
|
46
|
+
activeTab.value = id;
|
|
47
|
+
emit('update:modelValue', id);
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const classList = computed(() => [`base-tabs--${props.size}`]);
|
|
52
|
+
</script>
|
|
53
|
+
|
|
54
|
+
<style lang="scss" scoped>
|
|
55
|
+
@import '../../styles/variables';
|
|
56
|
+
@import '../../styles/root';
|
|
57
|
+
|
|
58
|
+
.base-tabs {
|
|
59
|
+
width: 100%;
|
|
60
|
+
|
|
61
|
+
&__header {
|
|
62
|
+
display: flex;
|
|
63
|
+
align-items: stretch;
|
|
64
|
+
position: relative;
|
|
65
|
+
z-index: 1;
|
|
66
|
+
|
|
67
|
+
&::before {
|
|
68
|
+
content: '';
|
|
69
|
+
width: 100%;
|
|
70
|
+
border-bottom: 1px solid var(--primary-black-200);
|
|
71
|
+
position: absolute;
|
|
72
|
+
bottom: 0;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
&__tab {
|
|
77
|
+
display: flex;
|
|
78
|
+
align-items: center;
|
|
79
|
+
padding: var(--spacing-m) var(--spacing-l);
|
|
80
|
+
background: none;
|
|
81
|
+
border: none;
|
|
82
|
+
cursor: pointer;
|
|
83
|
+
color: var(--primary-black-700);
|
|
84
|
+
transition: color 0.2s ease, border-color 0.2s ease;
|
|
85
|
+
white-space: nowrap;
|
|
86
|
+
position: relative;
|
|
87
|
+
|
|
88
|
+
@include hover {
|
|
89
|
+
& {
|
|
90
|
+
color: var(--primary-black);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
@include pressed {
|
|
95
|
+
color: var(--primary-black);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
@include is-disabled-state {
|
|
99
|
+
color: var(--primary-black-400);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
&--active {
|
|
103
|
+
color: var(--primary-blue);
|
|
104
|
+
border-bottom: 2px solid var(--primary-blue);
|
|
105
|
+
z-index: 2;
|
|
106
|
+
|
|
107
|
+
@include hover {
|
|
108
|
+
& {
|
|
109
|
+
color: var(--primary-blue-700);
|
|
110
|
+
border-color: transparent;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
@include pressed {
|
|
115
|
+
color: var(--primary-blue-deep);
|
|
116
|
+
border-color: transparent;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.base-tab__icon {
|
|
121
|
+
display: flex;
|
|
122
|
+
align-items: center;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Размеры
|
|
127
|
+
&--extra-small {
|
|
128
|
+
.base-tabs__header {
|
|
129
|
+
gap: var(--spacing-l);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.base-tabs__tab {
|
|
133
|
+
height: 38px;
|
|
134
|
+
padding: var(--spacing-s) var(--spacing-m);
|
|
135
|
+
font: var(--typography-text-s-medium);
|
|
136
|
+
gap: var(--spacing-s);
|
|
137
|
+
|
|
138
|
+
&--active {
|
|
139
|
+
font: var(--typography-text-s-semibold);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.base-tab__icon {
|
|
143
|
+
width: 20px;
|
|
144
|
+
height: 20px;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
&--small {
|
|
150
|
+
.base-tabs__header {
|
|
151
|
+
gap: var(--spacing-xl);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
.base-tabs__tab {
|
|
155
|
+
height: 48px;
|
|
156
|
+
padding: var(--spacing-m) var(--spacing-l);
|
|
157
|
+
font: var(--typography-text-m-medium);
|
|
158
|
+
gap: var(--spacing-m);
|
|
159
|
+
|
|
160
|
+
&--active {
|
|
161
|
+
font: var(--typography-text-m-semibold);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.base-tab__icon {
|
|
165
|
+
width: 24px;
|
|
166
|
+
height: 24px;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
&--medium {
|
|
172
|
+
.base-tabs__header {
|
|
173
|
+
gap: var(--spacing-xl);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.base-tabs__tab {
|
|
177
|
+
height: 56px;
|
|
178
|
+
padding: var(--spacing-l) var(--spacing-xl);
|
|
179
|
+
font: var(--typography-text-l-medium);
|
|
180
|
+
gap: var(--spacing-m);
|
|
181
|
+
|
|
182
|
+
&--active {
|
|
183
|
+
font: var(--typography-text-l-semibold);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
.base-tab__icon {
|
|
187
|
+
width: 32px;
|
|
188
|
+
height: 32px;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
</style>
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="base-textarea" :class="classList">
|
|
3
|
-
<
|
|
4
|
-
<span v-if="label" class="base-textarea__label-text">{{ label }}</span>
|
|
5
|
-
|
|
3
|
+
<div class="base-textarea__wrapper">
|
|
6
4
|
<div class="base-textarea__wrapper">
|
|
7
5
|
<textarea
|
|
8
6
|
:id="id"
|
|
@@ -17,11 +15,7 @@
|
|
|
17
15
|
{{ modelValue.length }}/{{ maxLength }}
|
|
18
16
|
</span>
|
|
19
17
|
</div>
|
|
20
|
-
|
|
21
|
-
<div v-if="(error && typeof error === 'string') || hint" class="base-textarea__hint">
|
|
22
|
-
{{ error || hint }}
|
|
23
|
-
</div>
|
|
24
|
-
</label>
|
|
18
|
+
</div>
|
|
25
19
|
</div>
|
|
26
20
|
</template>
|
|
27
21
|
|
|
@@ -34,8 +28,6 @@ import { useKitSize } from '../../composables/kit/size';
|
|
|
34
28
|
const props = withDefaults(defineProps<ICoreTextareaProps & { maxLength?: number }>(), {
|
|
35
29
|
modelValue: '',
|
|
36
30
|
placeholder: '' as string,
|
|
37
|
-
error: '',
|
|
38
|
-
hint: '',
|
|
39
31
|
size: 'medium',
|
|
40
32
|
});
|
|
41
33
|
|
|
@@ -73,11 +65,6 @@ function handleInput(event: Event) {
|
|
|
73
65
|
resize: none;
|
|
74
66
|
}
|
|
75
67
|
|
|
76
|
-
&__hint {
|
|
77
|
-
align-self: flex-start;
|
|
78
|
-
font: var(--typography-text-s-regular);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
68
|
&__wrapper {
|
|
82
69
|
display: flex;
|
|
83
70
|
position: relative;
|
|
@@ -113,10 +100,6 @@ function handleInput(event: Event) {
|
|
|
113
100
|
color: var(--primary-text-tertiary);
|
|
114
101
|
background: var(--primary-black-100);
|
|
115
102
|
}
|
|
116
|
-
|
|
117
|
-
#{$textarea}__hint {
|
|
118
|
-
color: var(--primary-text-secondary);
|
|
119
|
-
}
|
|
120
103
|
}
|
|
121
104
|
}
|
|
122
105
|
|
|
@@ -126,7 +109,7 @@ function handleInput(event: Event) {
|
|
|
126
109
|
color: var(--primary-text-quaternary);
|
|
127
110
|
}
|
|
128
111
|
|
|
129
|
-
&
|
|
112
|
+
&__wrapper {
|
|
130
113
|
display: flex;
|
|
131
114
|
flex-direction: column;
|
|
132
115
|
gap: 6px;
|
|
@@ -135,17 +118,7 @@ function handleInput(event: Event) {
|
|
|
135
118
|
height: 100%;
|
|
136
119
|
}
|
|
137
120
|
|
|
138
|
-
&__label-text {
|
|
139
|
-
align-self: flex-start;
|
|
140
|
-
font: var(--typography-text-s-medium);
|
|
141
|
-
color: var(--primary-text-primary);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
121
|
&.--is-error {
|
|
145
|
-
#{$textarea}__hint {
|
|
146
|
-
color: var(--error-red);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
122
|
#{$textarea}__field {
|
|
150
123
|
border-color: var(--error-red-light-01);
|
|
151
124
|
|
|
@@ -111,10 +111,12 @@ import BaseIcon from '../BaseIcon/BaseIcon.vue';
|
|
|
111
111
|
import { useModal } from '../../composables/useModal';
|
|
112
112
|
import type { UploadedFile, IpropsUpload } from '../../types/uploadedFile.d';
|
|
113
113
|
import ImageModal from './ImageModal.vue';
|
|
114
|
+
import CropModal from './CropModal.vue';
|
|
114
115
|
|
|
115
116
|
const props = withDefaults(defineProps<IpropsUpload>(), {
|
|
116
117
|
multiple: true,
|
|
117
118
|
maxFileSize: 25 * 1024 * 1024, // 25 MB в байтах
|
|
119
|
+
enableCrop: false,
|
|
118
120
|
});
|
|
119
121
|
|
|
120
122
|
const emit = defineEmits(['update:files']);
|
|
@@ -141,6 +143,11 @@ const triggerFileInput = () => {
|
|
|
141
143
|
fileInput.value?.click();
|
|
142
144
|
};
|
|
143
145
|
|
|
146
|
+
const isImageFile = (file: File) => {
|
|
147
|
+
const fileExtension = `.${file.name.split('.').pop()?.toLowerCase() || ''}`;
|
|
148
|
+
return mediaExtensions.includes(fileExtension);
|
|
149
|
+
};
|
|
150
|
+
|
|
144
151
|
const handleFileUpload = (event: Event) => {
|
|
145
152
|
const target = event.target as HTMLInputElement;
|
|
146
153
|
const files = target.files;
|
|
@@ -161,7 +168,11 @@ const handleFileUpload = (event: Event) => {
|
|
|
161
168
|
return;
|
|
162
169
|
}
|
|
163
170
|
|
|
164
|
-
|
|
171
|
+
if (props.enableCrop && isImageFile(file)) {
|
|
172
|
+
openCropModal(file);
|
|
173
|
+
} else {
|
|
174
|
+
uploadedFiles.value.push({ name: file.name, size: file.size, file });
|
|
175
|
+
}
|
|
165
176
|
});
|
|
166
177
|
|
|
167
178
|
isUploading.value = false;
|
|
@@ -169,6 +180,29 @@ const handleFileUpload = (event: Event) => {
|
|
|
169
180
|
}
|
|
170
181
|
};
|
|
171
182
|
|
|
183
|
+
const openCropModal = (file: File) => {
|
|
184
|
+
const imageUrl = URL.createObjectURL(file);
|
|
185
|
+
|
|
186
|
+
modal.open('crop-modal', {
|
|
187
|
+
closable: false,
|
|
188
|
+
imageUrl,
|
|
189
|
+
fileName: file.name,
|
|
190
|
+
cancelText: props.cropTexts?.cancel,
|
|
191
|
+
confirmText: props.cropTexts?.confirm,
|
|
192
|
+
onConfirm: (croppedFile: File) => {
|
|
193
|
+
uploadedFiles.value.push({
|
|
194
|
+
name: croppedFile.name,
|
|
195
|
+
size: croppedFile.size,
|
|
196
|
+
file: croppedFile
|
|
197
|
+
});
|
|
198
|
+
URL.revokeObjectURL(imageUrl);
|
|
199
|
+
},
|
|
200
|
+
onCancel: () => {
|
|
201
|
+
URL.revokeObjectURL(imageUrl);
|
|
202
|
+
}
|
|
203
|
+
}, CropModal);
|
|
204
|
+
};
|
|
205
|
+
|
|
172
206
|
const removeFile = (index: number, isMedia: boolean) => {
|
|
173
207
|
const files = isMedia ? mediaFiles.value : otherFiles.value;
|
|
174
208
|
const globalIndex = uploadedFiles.value.indexOf(files[index]);
|