dxd-style-code 0.1.10 → 0.1.12
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/dxd-style-code.js +9785 -8652
- package/dist/dxd-style-code.umd.cjs +3 -3
- package/dist/style.css +1 -1
- package/package.json +2 -2
- package/src/components/atoms/DX/DX.stories.js +265 -0
- package/src/components/atoms/DX/DX.vue +80 -0
- package/src/components/atoms/DX/index.js +2 -0
- package/src/components/atoms/DXAvatar/DXAvatar.stories.js +1 -2
- package/src/components/atoms/DXBackdrop/DXBackdrop.stories.js +77 -1
- package/src/components/atoms/DXBadge/DXBadge.stories.js +83 -1
- package/src/components/atoms/DXBlockquote/DXBlockquote.stories.js +99 -0
- package/src/components/{layout → atoms}/DXBox/DXBox.stories.js +2 -2
- package/src/components/{layout → atoms}/DXBox/DXBox.vue +69 -2
- package/src/components/atoms/DXButton/DXButton.stories.js +94 -1
- package/src/components/atoms/DXButton/DXButton.vue +145 -11
- package/src/components/atoms/DXCard/DXCard.stories.js +72 -14
- package/src/components/atoms/DXCard/DXCard.vue +3 -4
- package/src/components/atoms/DXCheckbox/DXCheckbox.stories.js +85 -1
- package/src/components/atoms/DXCode/DXCode.stories.js +95 -0
- package/src/components/{layout → atoms}/DXContainer/DXContainer.stories.js +2 -2
- package/src/components/{layout → atoms}/DXContainer/DXContainer.vue +28 -1
- package/src/components/atoms/DXDivider/DXDivider.stories.js +84 -1
- package/src/components/{layout → atoms}/DXFlex/DXFlex.stories.js +2 -2
- package/src/components/{layout → atoms}/DXFlex/DXFlex.vue +57 -3
- package/src/components/atoms/DXFormLabel/DXFormLabel.stories.js +70 -1
- package/src/components/{layout → atoms}/DXGrid/DXGrid.stories.js +2 -2
- package/src/components/atoms/DXHeading/DXHeading.stories.js +143 -0
- package/src/components/atoms/DXIcon/DXIcon.stories.js +74 -0
- package/src/components/atoms/DXIconWrapper/DXIconWrapper.stories.js +69 -0
- package/src/components/atoms/DXImage/DXImage.stories.js +483 -0
- package/src/components/atoms/DXImage/DXImage.vue +294 -0
- package/src/components/atoms/DXImage/index.js +2 -0
- package/src/components/atoms/DXInputAddon/DXInputAddon.stories.js +86 -1
- package/src/components/atoms/DXLabel/DXLabel.stories.js +101 -0
- package/src/components/atoms/DXLink/DXLink.stories.js +75 -10
- package/src/components/atoms/DXLink/DXLink.vue +59 -3
- package/src/components/atoms/DXList/DXList.stories.js +125 -0
- package/src/components/atoms/DXLoader/DXLoader.stories.js +75 -1
- package/src/components/atoms/DXNav/DXNav.stories.js +236 -0
- package/src/components/atoms/DXNav/DXNav.vue +114 -0
- package/src/components/atoms/DXNav/index.js +2 -0
- package/src/components/atoms/DXProgress/DXProgress.stories.js +76 -1
- package/src/components/atoms/DXRadio/DXRadio.stories.js +85 -1
- package/src/components/atoms/DXSkeleton/DXSkeleton.stories.js +59 -1
- package/src/components/atoms/DXSlider/DXSlider.stories.js +89 -0
- package/src/components/{layout → atoms}/DXSpacer/DXSpacer.stories.js +2 -2
- package/src/components/{layout → atoms}/DXStack/DXStack.stories.js +2 -2
- package/src/components/{layout → atoms}/DXStack/DXStack.vue +5 -2
- package/src/components/atoms/DXTags/DXTags.stories.js +77 -0
- package/src/components/atoms/DXText/DXText.stories.js +129 -0
- package/src/components/atoms/DXToast/DXToast.stories.js +64 -1
- package/src/components/atoms/DXToggle/DXToggle.stories.js +84 -1
- package/src/components/atoms/DXToggleButton/DXToggleButton.stories.js +78 -1
- package/src/components/atoms/DXTooltip/DXTooltip.stories.js +98 -1
- package/src/components/atoms/index.js +16 -1
- package/src/components/index.js +1 -7
- package/src/components/molecules/DXActionButtons/DXActionButtons.stories.js +280 -77
- package/src/components/molecules/DXActionButtons/DXActionButtons.vue +31 -31
- package/src/components/molecules/DXAlert/DXAlert.stories.js +199 -1
- package/src/components/molecules/DXAlert/DXAlert.vue +35 -13
- package/src/components/molecules/DXBreadcrumb/DXBreadcrumb.stories.js +125 -1
- package/src/components/molecules/DXBreadcrumb/DXBreadcrumb.vue +22 -18
- package/src/components/molecules/DXButtonGroup/DXButtonGroup.stories.js +193 -6
- package/src/components/molecules/DXButtonGroup/DXButtonGroup.vue +39 -3
- package/src/components/molecules/DXCloseButton/DXCloseButton.stories.js +64 -1
- package/src/components/molecules/DXComboBox/DXComboBox.stories.js +66 -0
- package/src/components/molecules/DXCopyField/DXCopyField.stories.js +128 -1
- package/src/components/molecules/DXCopyField/DXCopyField.vue +60 -7
- package/src/components/molecules/DXDataFilter/DXDataFilter.vue +8 -6
- package/src/components/molecules/DXDatePicker/DXDatePicker.stories.js +58 -0
- package/src/components/molecules/DXFileUpload/DXFileUpload.stories.js +66 -0
- package/src/components/molecules/DXFilterGroup/DXFilterGroup.stories.js +61 -0
- package/src/components/molecules/DXFormControl/DXFormControl.stories.js +76 -0
- package/src/components/molecules/DXFormControl/DXFormControl.vue +9 -8
- package/src/components/molecules/DXInput/DXInput.stories.js +100 -1
- package/src/components/molecules/DXInputGroup/DXInputGroup.stories.js +89 -1
- package/src/components/molecules/DXInputMask/DXInputMask.stories.js +67 -0
- package/src/components/molecules/DXMenu/DXMenu.stories.js +111 -4
- package/src/components/molecules/DXMenu/DXMenu.vue +18 -5
- package/src/components/molecules/DXMenu/README.md +1 -1
- package/src/components/molecules/DXPagination/DXPagination.stories.js +105 -2
- package/src/components/molecules/DXPagination/DXPagination.vue +18 -33
- package/src/components/molecules/DXPasswordInput/DXPasswordInput.stories.js +67 -1
- package/src/components/molecules/DXRadioCard/DXRadioCard.stories.js +64 -0
- package/src/components/molecules/DXRadioGroup/DXRadioGroup.stories.js +84 -0
- package/src/components/molecules/DXRating/DXRating.stories.js +3 -2
- package/src/components/molecules/DXSearchBar/DXSearchBar.stories.js +1 -1
- package/src/components/molecules/DXSearchBar/DXSearchBar.vue +16 -12
- package/src/components/molecules/DXSearchSelect/DXSearchSelect.stories.js +71 -0
- package/src/components/molecules/DXSegmentedControl/DXSegmentedControl.stories.js +74 -0
- package/src/components/molecules/DXSelect/DXSelect.stories.js +92 -1
- package/src/components/molecules/DXStatCard/DXStatCard.stories.js +1 -1
- package/src/components/molecules/DXStatCard/DXStatCard.vue +30 -26
- package/src/components/molecules/DXTableFiltersPanel/index.js +2 -0
- package/src/components/molecules/DXTablePagination/DXTablePagination.stories.js +67 -0
- package/src/components/molecules/DXTableToolbar/DXTableToolbar.stories.js +71 -0
- package/src/components/molecules/DXTextarea/DXTextarea.stories.js +87 -1
- package/src/components/molecules/DXTimePicker/DXTimePicker.stories.js +1 -1
- package/src/components/molecules/DXValidationIcon/DXValidationIcon.stories.js +59 -1
- package/src/components/molecules/index.js +0 -1
- package/src/components/organisms/DXAccordion/DXAccordion.stories.js +75 -0
- package/src/components/organisms/DXAppLayout/DXAppLayout.stories.js +28 -26
- package/src/components/organisms/DXAuthenticationForm/DXAuthenticationForm.stories.js +0 -2
- package/src/components/organisms/DXAuthenticationForm/DXAuthenticationForm.vue +5 -8
- package/src/components/{molecules → organisms}/DXBaseTable/DXBaseTable.stories.js +78 -1
- package/src/components/{molecules → organisms}/DXBaseTable/DXBaseTable.vue +2 -2
- package/src/components/organisms/DXChartContainer/DXChartContainer.stories.js +1 -1
- package/src/components/organisms/DXChartContainer/DXChartContainer.vue +10 -6
- package/src/components/organisms/DXChatInterface/DXChatInterface.stories.js +1 -1
- package/src/components/organisms/DXChatInterface/DXChatInterface.vue +6 -4
- package/src/components/organisms/DXCommentSection/DXCommentSection.stories.js +1 -1
- package/src/components/organisms/DXCommentSection/DXCommentSection.vue +7 -6
- package/src/components/organisms/DXDashboardGrid/DXDashboardGrid.stories.js +1 -1
- package/src/components/organisms/DXDashboardGrid/DXDashboardGrid.vue +4 -2
- package/src/components/organisms/DXDashboardWidget/DXDashboardWidget.stories.js +1 -1
- package/src/components/organisms/DXDashboardWidget/DXDashboardWidget.vue +3 -2
- package/src/components/organisms/DXDataTable/DXDataTable.stories.js +1 -1
- package/src/components/organisms/DXDropdown/DXDropdown.stories.js +84 -1
- package/src/components/organisms/DXEmptyState/DXEmptyState.stories.js +64 -0
- package/src/components/organisms/DXEmptyState/DXEmptyState.vue +4 -2
- package/src/components/organisms/DXHeaderBar/DXHeaderBar.stories.js +409 -3
- package/src/components/organisms/DXHeaderBar/DXHeaderBar.vue +262 -53
- package/src/components/organisms/DXMediaGallery/DXMediaGallery.stories.js +1 -1
- package/src/components/organisms/DXMediaGallery/DXMediaGallery.vue +7 -5
- package/src/components/organisms/DXModal/DXModal.stories.js +93 -1
- package/src/components/organisms/DXModal/DXModal.vue +3 -2
- package/src/components/organisms/DXNotificationCenter/DXNotificationCenter.stories.js +1 -1
- package/src/components/organisms/DXNotificationCenter/DXNotificationCenter.vue +2 -1
- package/src/components/organisms/DXReportGenerator/DXReportGenerator.vue +4 -3
- package/src/components/organisms/DXSettingsPanel/DXSettingsPanel.vue +11 -8
- package/src/components/organisms/DXSidebar/DXSidebar.stories.js +1 -1
- package/src/components/organisms/DXSidebarMenu/DXSidebarMenu.stories.js +104 -1
- package/src/components/organisms/DXSidebarMenu/DXSidebarMenu.vue +14 -4
- package/src/components/organisms/DXSidebarMenu/README.md +3 -3
- package/src/components/organisms/DXTable/DXTable.stories.js +138 -11
- package/src/components/organisms/DXTable/DXTable.vue +1 -1
- package/src/components/organisms/DXTabs/DXTabs.stories.js +91 -1
- package/src/components/organisms/DXUserProfileCard/DXUserProfileCard.stories.js +1 -1
- package/src/components/organisms/DXUserProfileCard/DXUserProfileCard.vue +20 -18
- package/src/components/organisms/index.js +1 -0
- package/src/components/utilities/DXAnimatePresence/DXAnimatePresence.stories.js +1 -1
- package/src/components/utilities/DXBreakpointProvider/DXBreakpointProvider.stories.js +2 -2
- package/src/components/utilities/DXObserver/DXObserver.stories.js +1 -1
- package/src/components/utilities/DXPortal/DXPortal.stories.js +1 -1
- package/src/components/utilities/DXStaggeredAnimation/DXStaggeredAnimation.stories.js +2 -2
- package/src/components/utilities/DXThemeProvider/DXThemeProvider.stories.js +1 -1
- package/src/components/utilities/DXTransitionGroup/DXTransitionGroup.stories.js +1 -1
- package/src/composables/useSize.js +8 -1
- package/src/index.js +1 -7
- package/src/components/layout/index.js +0 -8
- package/src/components/typography/DXBlockquote/DXBlockquote.stories.js +0 -33
- package/src/components/typography/DXCode/DXCode.stories.js +0 -29
- package/src/components/typography/DXHeading/DXHeading.stories.js +0 -54
- package/src/components/typography/DXLabel/DXLabel.stories.js +0 -40
- package/src/components/typography/DXList/DXList.stories.js +0 -50
- package/src/components/typography/DXText/DXText.stories.js +0 -47
- package/src/components/typography/index.js +0 -8
- /package/src/components/{typography → atoms}/DXBlockquote/DXBlockquote.vue +0 -0
- /package/src/components/{typography → atoms}/DXBlockquote/index.js +0 -0
- /package/src/components/{layout → atoms}/DXBox/index.js +0 -0
- /package/src/components/{typography → atoms}/DXCode/DXCode.vue +0 -0
- /package/src/components/{typography → atoms}/DXCode/index.js +0 -0
- /package/src/components/{layout → atoms}/DXContainer/index.js +0 -0
- /package/src/components/{layout → atoms}/DXFlex/index.js +0 -0
- /package/src/components/{layout → atoms}/DXGrid/DXGrid.vue +0 -0
- /package/src/components/{layout → atoms}/DXGrid/index.js +0 -0
- /package/src/components/{typography → atoms}/DXHeading/DXHeading.vue +0 -0
- /package/src/components/{typography → atoms}/DXHeading/index.js +0 -0
- /package/src/components/{typography → atoms}/DXLabel/DXLabel.vue +0 -0
- /package/src/components/{typography → atoms}/DXLabel/index.js +0 -0
- /package/src/components/{typography → atoms}/DXList/DXList.vue +0 -0
- /package/src/components/{typography → atoms}/DXList/index.js +0 -0
- /package/src/components/{layout → atoms}/DXSpacer/DXSpacer.vue +0 -0
- /package/src/components/{layout → atoms}/DXSpacer/index.js +0 -0
- /package/src/components/{layout → atoms}/DXStack/index.js +0 -0
- /package/src/components/{typography → atoms}/DXText/DXText.vue +0 -0
- /package/src/components/{typography → atoms}/DXText/index.js +0 -0
- /package/src/components/{molecules → organisms}/DXBaseTable/index.js +0 -0
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
:class="containerClasses"
|
|
4
|
+
data-component="DXImage"
|
|
5
|
+
:data-size="size"
|
|
6
|
+
:data-object-fit="objectFit"
|
|
7
|
+
:data-rounded="rounded"
|
|
8
|
+
:style="containerStyle"
|
|
9
|
+
>
|
|
10
|
+
<!-- Lazy loading wrapper -->
|
|
11
|
+
<DXObserver
|
|
12
|
+
v-if="lazy"
|
|
13
|
+
:threshold="0.1"
|
|
14
|
+
:once="true"
|
|
15
|
+
@intersect="handleIntersect"
|
|
16
|
+
>
|
|
17
|
+
<template v-if="isVisible">
|
|
18
|
+
<img
|
|
19
|
+
v-if="currentSrc && !imgError"
|
|
20
|
+
:src="currentSrc"
|
|
21
|
+
:alt="alt"
|
|
22
|
+
:srcset="srcset"
|
|
23
|
+
:sizes="sizes"
|
|
24
|
+
:class="imageClasses"
|
|
25
|
+
:style="imageStyle"
|
|
26
|
+
@load="handleLoad"
|
|
27
|
+
@error="handleError"
|
|
28
|
+
/>
|
|
29
|
+
<slot v-else-if="$slots.fallback" name="fallback" />
|
|
30
|
+
<div v-else-if="fallback && !fallbackError" class="w-full h-full">
|
|
31
|
+
<img
|
|
32
|
+
:src="fallback"
|
|
33
|
+
:alt="alt"
|
|
34
|
+
:class="imageClasses"
|
|
35
|
+
:style="imageStyle"
|
|
36
|
+
@error="fallbackError = true"
|
|
37
|
+
/>
|
|
38
|
+
</div>
|
|
39
|
+
<div v-else :class="defaultFallbackClasses">
|
|
40
|
+
<DXIcon :icon="PhotoIcon" :size="iconSize" class="text-slate-400" />
|
|
41
|
+
</div>
|
|
42
|
+
</template>
|
|
43
|
+
<div v-else-if="showSkeleton" :class="skeletonClasses">
|
|
44
|
+
<slot name="placeholder">
|
|
45
|
+
<div class="w-full h-full bg-slate-200 animate-pulse" />
|
|
46
|
+
</slot>
|
|
47
|
+
</div>
|
|
48
|
+
</DXObserver>
|
|
49
|
+
|
|
50
|
+
<!-- Non-lazy loading -->
|
|
51
|
+
<template v-else>
|
|
52
|
+
<img
|
|
53
|
+
v-if="currentSrc && !imgError"
|
|
54
|
+
:src="currentSrc"
|
|
55
|
+
:alt="alt"
|
|
56
|
+
:srcset="srcset"
|
|
57
|
+
:sizes="sizes"
|
|
58
|
+
:class="imageClasses"
|
|
59
|
+
:style="imageStyle"
|
|
60
|
+
@load="handleLoad"
|
|
61
|
+
@error="handleError"
|
|
62
|
+
/>
|
|
63
|
+
<slot v-else-if="$slots.fallback" name="fallback" />
|
|
64
|
+
<div v-else-if="fallback && !fallbackError" class="w-full h-full">
|
|
65
|
+
<img
|
|
66
|
+
:src="fallback"
|
|
67
|
+
:alt="alt"
|
|
68
|
+
:class="imageClasses"
|
|
69
|
+
:style="imageStyle"
|
|
70
|
+
@error="fallbackError = true"
|
|
71
|
+
/>
|
|
72
|
+
</div>
|
|
73
|
+
<div v-else :class="defaultFallbackClasses">
|
|
74
|
+
<DXIcon :icon="PhotoIcon" :size="iconSize" class="text-slate-400" />
|
|
75
|
+
</div>
|
|
76
|
+
</template>
|
|
77
|
+
</div>
|
|
78
|
+
</template>
|
|
79
|
+
|
|
80
|
+
<script setup>
|
|
81
|
+
import { ref, computed, watch } from "vue";
|
|
82
|
+
import { PhotoIcon } from "@heroicons/vue/24/outline";
|
|
83
|
+
import { useSize } from "../../../composables/useSize";
|
|
84
|
+
import { useClassComposition } from "../../../composables/useClassComposition";
|
|
85
|
+
import DXObserver from "../../utilities/DXObserver/DXObserver.vue";
|
|
86
|
+
import DXIcon from "../DXIcon/DXIcon.vue";
|
|
87
|
+
|
|
88
|
+
const props = defineProps({
|
|
89
|
+
/** URL изображения */
|
|
90
|
+
src: { type: String, required: true },
|
|
91
|
+
/** Alt текст для доступности */
|
|
92
|
+
alt: { type: String, default: "" },
|
|
93
|
+
/** Ширина изображения (кастомная) */
|
|
94
|
+
width: { type: [String, Number], default: null },
|
|
95
|
+
/** Высота изображения (кастомная) */
|
|
96
|
+
height: { type: [String, Number], default: null },
|
|
97
|
+
/** Предопределенный размер: xs | sm | md | lg | xl */
|
|
98
|
+
size: { type: String, default: null },
|
|
99
|
+
/** object-fit: cover | contain | fill | none | scale-down */
|
|
100
|
+
objectFit: { type: String, default: "cover", validator: (v) => ["cover", "contain", "fill", "none", "scale-down"].includes(v) },
|
|
101
|
+
/** Lazy loading через DXObserver */
|
|
102
|
+
lazy: { type: Boolean, default: false },
|
|
103
|
+
/** URL fallback изображения при ошибке */
|
|
104
|
+
fallback: { type: String, default: null },
|
|
105
|
+
/** URL placeholder изображения */
|
|
106
|
+
placeholder: { type: String, default: null },
|
|
107
|
+
/** Показывать skeleton при загрузке */
|
|
108
|
+
showSkeleton: { type: Boolean, default: true },
|
|
109
|
+
/** Скругление: none | sm | md | lg | xl | full */
|
|
110
|
+
rounded: { type: String, default: "none", validator: (v) => ["none", "sm", "md", "lg", "xl", "full"].includes(v) },
|
|
111
|
+
/** Responsive изображения (srcset атрибут) */
|
|
112
|
+
srcset: { type: String, default: null },
|
|
113
|
+
/** Sizes атрибут для responsive */
|
|
114
|
+
sizes: { type: String, default: null },
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
const emit = defineEmits(["load", "error"]);
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Флаг ошибки загрузки основного изображения
|
|
121
|
+
*/
|
|
122
|
+
const imgError = ref(false);
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Флаг ошибки загрузки fallback изображения
|
|
126
|
+
*/
|
|
127
|
+
const fallbackError = ref(false);
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Флаг видимости для lazy loading
|
|
131
|
+
*/
|
|
132
|
+
const isVisible = ref(false);
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Текущий источник изображения
|
|
136
|
+
*
|
|
137
|
+
* @description
|
|
138
|
+
* Определяет какой источник использовать:
|
|
139
|
+
* - Если есть placeholder и изображение еще не загружено (lazy режим) → placeholder
|
|
140
|
+
* - Иначе → основной src
|
|
141
|
+
*/
|
|
142
|
+
const currentSrc = computed(() => {
|
|
143
|
+
if (props.placeholder && props.lazy && !isVisible.value) {
|
|
144
|
+
return props.placeholder;
|
|
145
|
+
}
|
|
146
|
+
return props.src;
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Стили для контейнера
|
|
151
|
+
*
|
|
152
|
+
* @description
|
|
153
|
+
* Вычисляет inline стили для контейнера на основе width и height props.
|
|
154
|
+
* Если указан size, размеры берутся из useSize.
|
|
155
|
+
*/
|
|
156
|
+
const containerStyle = computed(() => {
|
|
157
|
+
const style = {};
|
|
158
|
+
|
|
159
|
+
if (props.width) {
|
|
160
|
+
style.width = typeof props.width === 'number' ? `${props.width}px` : props.width;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (props.height) {
|
|
164
|
+
style.height = typeof props.height === 'number' ? `${props.height}px` : props.height;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return Object.keys(style).length > 0 ? style : null;
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Стили для изображения
|
|
172
|
+
*/
|
|
173
|
+
const imageStyle = computed(() => {
|
|
174
|
+
return null;
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Классы для контейнера
|
|
179
|
+
*/
|
|
180
|
+
const containerClasses = computed(() =>
|
|
181
|
+
useClassComposition(
|
|
182
|
+
"relative inline-block overflow-hidden",
|
|
183
|
+
props.size ? useSize(props.size, 'image') : null,
|
|
184
|
+
{
|
|
185
|
+
"rounded-sm": props.rounded === "sm",
|
|
186
|
+
"rounded-md": props.rounded === "md",
|
|
187
|
+
"rounded-lg": props.rounded === "lg",
|
|
188
|
+
"rounded-xl": props.rounded === "xl",
|
|
189
|
+
"rounded-full": props.rounded === "full",
|
|
190
|
+
}
|
|
191
|
+
)
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Классы для изображения
|
|
196
|
+
*/
|
|
197
|
+
const imageClasses = computed(() => {
|
|
198
|
+
const objectFitClasses = {
|
|
199
|
+
cover: "object-cover",
|
|
200
|
+
contain: "object-contain",
|
|
201
|
+
fill: "object-fill",
|
|
202
|
+
none: "object-none",
|
|
203
|
+
"scale-down": "object-scale-down",
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
return useClassComposition(
|
|
207
|
+
"w-full h-full",
|
|
208
|
+
objectFitClasses[props.objectFit] || objectFitClasses.cover
|
|
209
|
+
);
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Классы для skeleton
|
|
214
|
+
*/
|
|
215
|
+
const skeletonClasses = computed(() =>
|
|
216
|
+
useClassComposition("w-full h-full")
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Классы для дефолтного fallback
|
|
221
|
+
*/
|
|
222
|
+
const defaultFallbackClasses = computed(() =>
|
|
223
|
+
useClassComposition(
|
|
224
|
+
"w-full h-full bg-slate-100 flex items-center justify-center"
|
|
225
|
+
)
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Размер иконки для fallback
|
|
230
|
+
*/
|
|
231
|
+
const iconSize = computed(() => {
|
|
232
|
+
if (props.size) {
|
|
233
|
+
const sizeMap = {
|
|
234
|
+
xs: "sm",
|
|
235
|
+
sm: "md",
|
|
236
|
+
md: "lg",
|
|
237
|
+
lg: "xl",
|
|
238
|
+
xl: "xl",
|
|
239
|
+
};
|
|
240
|
+
return sizeMap[props.size] || "lg";
|
|
241
|
+
}
|
|
242
|
+
return "lg";
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Обработчик успешной загрузки изображения
|
|
247
|
+
*/
|
|
248
|
+
function handleLoad() {
|
|
249
|
+
emit("load");
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Обработчик ошибки загрузки изображения
|
|
254
|
+
*/
|
|
255
|
+
function handleError() {
|
|
256
|
+
imgError.value = true;
|
|
257
|
+
emit("error");
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Обработчик пересечения для lazy loading
|
|
262
|
+
*/
|
|
263
|
+
function handleIntersect(intersecting) {
|
|
264
|
+
if (intersecting) {
|
|
265
|
+
isVisible.value = true;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Инициализация для non-lazy режима
|
|
270
|
+
if (!props.lazy) {
|
|
271
|
+
isVisible.value = true;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Сбрасывает ошибки при изменении src
|
|
276
|
+
*/
|
|
277
|
+
watch(() => props.src, () => {
|
|
278
|
+
imgError.value = false;
|
|
279
|
+
fallbackError.value = false;
|
|
280
|
+
if (!props.lazy) {
|
|
281
|
+
isVisible.value = true;
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Сбрасывает видимость при изменении lazy
|
|
287
|
+
*/
|
|
288
|
+
watch(() => props.lazy, (newVal) => {
|
|
289
|
+
if (!newVal) {
|
|
290
|
+
isVisible.value = true;
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
</script>
|
|
294
|
+
|
|
@@ -12,7 +12,92 @@ import {
|
|
|
12
12
|
export default {
|
|
13
13
|
title: 'Atoms/DXInputAddon',
|
|
14
14
|
component: DXInputAddon,
|
|
15
|
-
tags: ['autodocs'],
|
|
15
|
+
tags: ['autodocs', 'category:form'],
|
|
16
|
+
parameters: {
|
|
17
|
+
docs: {
|
|
18
|
+
description: {
|
|
19
|
+
component: `
|
|
20
|
+
# DXInputAddon
|
|
21
|
+
|
|
22
|
+
Компонент аддона для групп полей ввода, используемый для добавления префиксов и суффиксов к полям.
|
|
23
|
+
|
|
24
|
+
## Назначение
|
|
25
|
+
|
|
26
|
+
DXInputAddon предоставляет способ добавления дополнительных элементов (текста, иконок) слева или справа
|
|
27
|
+
от полей ввода в составе DXInputGroup. Компонент автоматически синхронизирует размер, состояние disabled/error
|
|
28
|
+
и правильно применяет border-radius для визуального единства группы.
|
|
29
|
+
|
|
30
|
+
## Архитектура
|
|
31
|
+
|
|
32
|
+
### Использует
|
|
33
|
+
- \`useSize\` composable - для унификации размеров (синхронизируется с DXInputGroup)
|
|
34
|
+
- \`provide/inject\` - для интеграции с DXInputGroup
|
|
35
|
+
|
|
36
|
+
### Используется в
|
|
37
|
+
- \`DXInputGroup\` - группы полей с аддонами
|
|
38
|
+
- Префиксы для URL (https://)
|
|
39
|
+
- Суффиксы для валют (USD, EUR)
|
|
40
|
+
- Иконки в группах полей
|
|
41
|
+
- Любые места, требующие дополнительных элементов рядом с полями
|
|
42
|
+
|
|
43
|
+
## Внутренняя логика
|
|
44
|
+
|
|
45
|
+
### Интеграция с DXInputGroup
|
|
46
|
+
При использовании внутри \`DXInputGroup\`:
|
|
47
|
+
- Аддон автоматически регистрируется в группе через \`provide/inject\`
|
|
48
|
+
- Размер синхронизируется с размером группы
|
|
49
|
+
- Состояние \`disabled\` наследуется от группы
|
|
50
|
+
- Состояние \`error\` синхронизируется с группой (красная рамка)
|
|
51
|
+
- Border-radius адаптируется в зависимости от позиции:
|
|
52
|
+
- Первый аддон → \`rounded-l-xl\`
|
|
53
|
+
- Последний аддон → \`rounded-r-xl\`
|
|
54
|
+
- Средний аддон → \`rounded-none\`
|
|
55
|
+
|
|
56
|
+
### Содержимое
|
|
57
|
+
Аддон может содержать:
|
|
58
|
+
- Текст через default slot (например, "https://", "USD")
|
|
59
|
+
- Иконку через \`icon\` prop (Heroicon компонент)
|
|
60
|
+
- Комбинацию текста и иконки
|
|
61
|
+
|
|
62
|
+
### Визуальное оформление
|
|
63
|
+
- Фон: \`bg-slate-50\` (светло-серый)
|
|
64
|
+
- Текст: \`text-slate-600\`
|
|
65
|
+
- Рамка: синхронизируется с группой
|
|
66
|
+
- Padding: адаптируется к размеру группы
|
|
67
|
+
|
|
68
|
+
## Особенности
|
|
69
|
+
|
|
70
|
+
### Позиционирование
|
|
71
|
+
- Аддоны размещаются слева или справа от основного поля
|
|
72
|
+
- Может быть несколько аддонов (например, $ слева и USD справа)
|
|
73
|
+
|
|
74
|
+
### Размеры
|
|
75
|
+
Размер автоматически синхронизируется с размером DXInputGroup:
|
|
76
|
+
- **sm** - маленький размер
|
|
77
|
+
- **md** - средний размер (по умолчанию)
|
|
78
|
+
- **lg** - крупный размер
|
|
79
|
+
|
|
80
|
+
### Состояния
|
|
81
|
+
- **disabled** - наследуется от группы, аддон становится полупрозрачным
|
|
82
|
+
- **error** - наследуется от группы, рамка становится красной
|
|
83
|
+
|
|
84
|
+
### Иконки
|
|
85
|
+
- Иконка отображается слева от текста (если есть)
|
|
86
|
+
- Размер иконки: \`w-4 h-4\`
|
|
87
|
+
- Можно использовать только иконку без текста
|
|
88
|
+
|
|
89
|
+
### Использование
|
|
90
|
+
Аддоны всегда используются внутри DXInputGroup:
|
|
91
|
+
\`\`\`
|
|
92
|
+
<DXInputGroup label="Website">
|
|
93
|
+
<DXInputAddon>https://</DXInputAddon>
|
|
94
|
+
<DXInput v-model="url" placeholder="example.com" />
|
|
95
|
+
</DXInputGroup>
|
|
96
|
+
\`\`\`
|
|
97
|
+
`,
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
},
|
|
16
101
|
argTypes: {
|
|
17
102
|
icon: {
|
|
18
103
|
control: false,
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import DXLabel from './DXLabel.vue';
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
title: 'Atoms/DXLabel',
|
|
5
|
+
component: DXLabel,
|
|
6
|
+
tags: ['autodocs', 'category:typography'],
|
|
7
|
+
parameters: {
|
|
8
|
+
docs: {
|
|
9
|
+
description: {
|
|
10
|
+
component: `
|
|
11
|
+
# DXLabel
|
|
12
|
+
|
|
13
|
+
Компонент лейбла для форм с поддержкой различных размеров, весов и цветов.
|
|
14
|
+
|
|
15
|
+
## Назначение
|
|
16
|
+
|
|
17
|
+
DXLabel предоставляет стандартизированный лейбл для форм с настраиваемыми параметрами типографики.
|
|
18
|
+
Компонент поддерживает различные размеры, веса шрифта, цвета и автоматическое отображение индикатора
|
|
19
|
+
обязательного поля.
|
|
20
|
+
|
|
21
|
+
## Архитектура
|
|
22
|
+
|
|
23
|
+
### Использует
|
|
24
|
+
- \`useVariantText\` composable - для цветовых вариантов
|
|
25
|
+
|
|
26
|
+
### Используется в
|
|
27
|
+
- Формы с полями ввода
|
|
28
|
+
- Связанные с input элементами лейблы
|
|
29
|
+
- Любые места, требующие текстовых меток
|
|
30
|
+
|
|
31
|
+
## Внутренняя логика
|
|
32
|
+
|
|
33
|
+
### Размеры
|
|
34
|
+
Поддерживает 4 размера:
|
|
35
|
+
- **xs** - очень маленький текст
|
|
36
|
+
- **sm** - маленький текст (по умолчанию)
|
|
37
|
+
- **md** - средний текст
|
|
38
|
+
- **lg** - большой текст
|
|
39
|
+
|
|
40
|
+
### Веса шрифта
|
|
41
|
+
Поддерживает 3 веса:
|
|
42
|
+
- **normal** - обычный вес
|
|
43
|
+
- **medium** - средний вес (по умолчанию)
|
|
44
|
+
- **semibold** - полужирный
|
|
45
|
+
|
|
46
|
+
### Цвета
|
|
47
|
+
Поддерживает 3 цветовых варианта:
|
|
48
|
+
- **default** - цвет по умолчанию
|
|
49
|
+
- **muted** - приглушенный цвет
|
|
50
|
+
- **primary** - основной цвет
|
|
51
|
+
|
|
52
|
+
### Обязательное поле
|
|
53
|
+
При \`required={true}\` автоматически добавляется красная звездочка (*) справа от текста.
|
|
54
|
+
|
|
55
|
+
## Особенности
|
|
56
|
+
|
|
57
|
+
### Связь с input
|
|
58
|
+
Поддерживает \`htmlFor\` prop для связи с input элементом через \`id\`:
|
|
59
|
+
- Улучшает доступность
|
|
60
|
+
- Позволяет кликать на лейбл для фокуса на input
|
|
61
|
+
|
|
62
|
+
### Семантика
|
|
63
|
+
Использует нативный HTML \`<label>\` тег для правильной семантики и доступности.
|
|
64
|
+
`,
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export const Default = {
|
|
71
|
+
args: {},
|
|
72
|
+
render: (args) => ({
|
|
73
|
+
components: { DXLabel },
|
|
74
|
+
setup() { return { args }; },
|
|
75
|
+
template: '<DXLabel v-bind="args">Email адрес</DXLabel>',
|
|
76
|
+
}),
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export const Required = {
|
|
80
|
+
args: { required: true },
|
|
81
|
+
render: (args) => ({
|
|
82
|
+
components: { DXLabel },
|
|
83
|
+
setup() { return { args }; },
|
|
84
|
+
template: '<DXLabel v-bind="args">Обязательное поле</DXLabel>',
|
|
85
|
+
}),
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
export const Sizes = {
|
|
89
|
+
render: () => ({
|
|
90
|
+
components: { DXLabel },
|
|
91
|
+
template: `
|
|
92
|
+
<div class="space-y-2">
|
|
93
|
+
<DXLabel size="xs">Extra Small Label</DXLabel>
|
|
94
|
+
<DXLabel size="sm">Small Label (default)</DXLabel>
|
|
95
|
+
<DXLabel size="md">Medium Label</DXLabel>
|
|
96
|
+
<DXLabel size="lg">Large Label</DXLabel>
|
|
97
|
+
</div>
|
|
98
|
+
`,
|
|
99
|
+
}),
|
|
100
|
+
};
|
|
101
|
+
|
|
@@ -5,7 +5,7 @@ import { HomeIcon } from '@heroicons/vue/24/outline';
|
|
|
5
5
|
export default {
|
|
6
6
|
title: 'Atoms/DXLink',
|
|
7
7
|
component: DXLink,
|
|
8
|
-
tags: ['autodocs'],
|
|
8
|
+
tags: ['autodocs', 'category:navigation'],
|
|
9
9
|
parameters: {
|
|
10
10
|
docs: {
|
|
11
11
|
description: {
|
|
@@ -51,10 +51,13 @@ DXLink - атомарный компонент для создания ссыл
|
|
|
51
51
|
- \`rel="noopener noreferrer"\` - для защиты от tabnabbing атак
|
|
52
52
|
- Иконка внешней ссылки (если \`showExternalIcon={true}\`)
|
|
53
53
|
|
|
54
|
-
###
|
|
55
|
-
-
|
|
56
|
-
-
|
|
57
|
-
|
|
54
|
+
### Варианты стилизации
|
|
55
|
+
- \`link\` (по умолчанию) - обычные текстовые ссылки без padding и border-radius
|
|
56
|
+
- \`primary\`, \`secondary\`, \`ghost\`, \`outline\` - варианты с небольшими отступами (padding) и скруглениями (rounded-lg)
|
|
57
|
+
|
|
58
|
+
### Состояния
|
|
59
|
+
- **disabled**: Полностью отключает ссылку - блокирует клик, применяет визуальные стили (opacity, cursor-not-allowed), отключает pointer-events
|
|
60
|
+
- **inactive**: Визуально приглушенная ссылка, но остается кликабельной. Используется для текущих страниц, неактивных элементов навигации
|
|
58
61
|
|
|
59
62
|
## Особенности
|
|
60
63
|
|
|
@@ -72,11 +75,23 @@ DXLink - атомарный компонент для создания ссыл
|
|
|
72
75
|
<DXLink href="/about">О нас</DXLink>
|
|
73
76
|
\`\`\`
|
|
74
77
|
|
|
78
|
+
## Особенности
|
|
79
|
+
|
|
80
|
+
### Padding и Border-radius
|
|
81
|
+
Варианты (кроме \`link\`) автоматически получают:
|
|
82
|
+
- Небольшие отступы в зависимости от размера (xs: px-2 py-0.5, sm: px-2.5 py-1, md: px-3 py-1.5, lg: px-3.5 py-2, xl: px-4 py-2.5)
|
|
83
|
+
- Скругления \`rounded-lg\`
|
|
84
|
+
|
|
85
|
+
### Разница между disabled и inactive
|
|
86
|
+
- **disabled**: Ссылка полностью неактивна, не кликабельна, визуально затемнена
|
|
87
|
+
- **inactive**: Ссылка визуально приглушена, но остается кликабельной. Подходит для текущих страниц в навигации
|
|
88
|
+
|
|
75
89
|
## Ограничения
|
|
76
90
|
|
|
77
91
|
- Не используйте одновременно \`href\` и \`to\` - приоритет у \`to\`
|
|
78
92
|
- Для Vue Router требуется установленный \`vue-router\`
|
|
79
93
|
- Disabled ссылки не кликабельны, но остаются в DOM
|
|
94
|
+
- Inactive ссылки кликабельны, но визуально приглушены
|
|
80
95
|
`,
|
|
81
96
|
},
|
|
82
97
|
},
|
|
@@ -84,8 +99,8 @@ DXLink - атомарный компонент для создания ссыл
|
|
|
84
99
|
argTypes: {
|
|
85
100
|
variant: {
|
|
86
101
|
control: 'select',
|
|
87
|
-
options: ['primary', 'secondary', 'ghost', '
|
|
88
|
-
description: 'Вариант стилизации ссылки.
|
|
102
|
+
options: ['link', 'primary', 'secondary', 'ghost', 'outline'],
|
|
103
|
+
description: 'Вариант стилизации ссылки. "link" - обычная ссылка без padding, остальные - с padding и border-radius.',
|
|
89
104
|
table: {
|
|
90
105
|
type: { summary: 'string' },
|
|
91
106
|
defaultValue: { summary: 'link' },
|
|
@@ -104,7 +119,16 @@ DXLink - атомарный компонент для создания ссыл
|
|
|
104
119
|
},
|
|
105
120
|
disabled: {
|
|
106
121
|
control: 'boolean',
|
|
107
|
-
description: 'Отключенное состояние. Блокирует клик и применяет визуальные стили.',
|
|
122
|
+
description: 'Отключенное состояние. Блокирует клик и применяет визуальные стили. Ссылка не кликабельна.',
|
|
123
|
+
table: {
|
|
124
|
+
type: { summary: 'boolean' },
|
|
125
|
+
defaultValue: { summary: 'false' },
|
|
126
|
+
category: 'Behavior',
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
inactive: {
|
|
130
|
+
control: 'boolean',
|
|
131
|
+
description: 'Неактивное состояние. Визуально приглушенная ссылка, но остается кликабельной. Используется для текущих страниц.',
|
|
108
132
|
table: {
|
|
109
133
|
type: { summary: 'boolean' },
|
|
110
134
|
defaultValue: { summary: 'false' },
|
|
@@ -192,7 +216,7 @@ export const Variants = {
|
|
|
192
216
|
parameters: {
|
|
193
217
|
docs: {
|
|
194
218
|
description: {
|
|
195
|
-
story: 'Все доступные варианты стилизации ссылок. Вариант "link" - стандартный для текстовых
|
|
219
|
+
story: 'Все доступные варианты стилизации ссылок. Вариант "link" - стандартный для текстовых ссылок без padding и border-radius. Остальные варианты имеют небольшие отступы и скругления, делая ссылку похожей на кнопку.',
|
|
196
220
|
},
|
|
197
221
|
},
|
|
198
222
|
},
|
|
@@ -207,10 +231,14 @@ export const Variants = {
|
|
|
207
231
|
<DXLink href="/primary" variant="primary">Primary</DXLink>
|
|
208
232
|
<DXLink href="/secondary" variant="secondary">Secondary</DXLink>
|
|
209
233
|
<DXLink href="/ghost" variant="ghost">Ghost</DXLink>
|
|
234
|
+
<DXLink href="/outline" variant="outline">Outline</DXLink>
|
|
210
235
|
</div>
|
|
211
236
|
</div>
|
|
212
237
|
<div class="text-sm text-slate-600">
|
|
213
|
-
Все варианты: link | primary | secondary | ghost
|
|
238
|
+
Все варианты: link | primary | secondary | ghost | outline
|
|
239
|
+
</div>
|
|
240
|
+
<div class="text-xs text-slate-500">
|
|
241
|
+
Варианты (кроме link) имеют padding и border-radius
|
|
214
242
|
</div>
|
|
215
243
|
</div>
|
|
216
244
|
`,
|
|
@@ -357,6 +385,43 @@ export const Disabled = {
|
|
|
357
385
|
}),
|
|
358
386
|
};
|
|
359
387
|
|
|
388
|
+
export const Inactive = {
|
|
389
|
+
parameters: {
|
|
390
|
+
docs: {
|
|
391
|
+
description: {
|
|
392
|
+
story: 'Неактивное состояние ссылки. Визуально приглушенная ссылка (opacity, приглушенные цвета), но остается кликабельной. Используется для текущих страниц в навигации, breadcrumb и других случаях, когда нужно показать неактивный элемент, но сохранить возможность клика.',
|
|
393
|
+
},
|
|
394
|
+
},
|
|
395
|
+
},
|
|
396
|
+
render: () => ({
|
|
397
|
+
components: { DXLink },
|
|
398
|
+
template: `
|
|
399
|
+
<div class="flex flex-col items-start gap-4">
|
|
400
|
+
<div class="flex flex-col gap-4">
|
|
401
|
+
<h3 class="text-sm font-semibold text-slate-700">Неактивные ссылки</h3>
|
|
402
|
+
<div class="flex items-center gap-4 flex-wrap">
|
|
403
|
+
<DXLink href="/current" inactive variant="link">
|
|
404
|
+
Текущая страница (link)
|
|
405
|
+
</DXLink>
|
|
406
|
+
<DXLink href="/current" inactive variant="primary">
|
|
407
|
+
Текущая страница (primary)
|
|
408
|
+
</DXLink>
|
|
409
|
+
<DXLink href="/current" inactive variant="ghost">
|
|
410
|
+
Текущая страница (ghost)
|
|
411
|
+
</DXLink>
|
|
412
|
+
</div>
|
|
413
|
+
<div class="text-sm text-slate-600">
|
|
414
|
+
Неактивные ссылки визуально приглушены, но остаются кликабельными
|
|
415
|
+
</div>
|
|
416
|
+
<div class="text-xs text-slate-500">
|
|
417
|
+
Используется для текущих страниц в breadcrumb и навигации
|
|
418
|
+
</div>
|
|
419
|
+
</div>
|
|
420
|
+
</div>
|
|
421
|
+
`,
|
|
422
|
+
}),
|
|
423
|
+
};
|
|
424
|
+
|
|
360
425
|
export const WithIcons = {
|
|
361
426
|
parameters: {
|
|
362
427
|
docs: {
|