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.
Files changed (178) hide show
  1. package/dist/dxd-style-code.js +9785 -8652
  2. package/dist/dxd-style-code.umd.cjs +3 -3
  3. package/dist/style.css +1 -1
  4. package/package.json +2 -2
  5. package/src/components/atoms/DX/DX.stories.js +265 -0
  6. package/src/components/atoms/DX/DX.vue +80 -0
  7. package/src/components/atoms/DX/index.js +2 -0
  8. package/src/components/atoms/DXAvatar/DXAvatar.stories.js +1 -2
  9. package/src/components/atoms/DXBackdrop/DXBackdrop.stories.js +77 -1
  10. package/src/components/atoms/DXBadge/DXBadge.stories.js +83 -1
  11. package/src/components/atoms/DXBlockquote/DXBlockquote.stories.js +99 -0
  12. package/src/components/{layout → atoms}/DXBox/DXBox.stories.js +2 -2
  13. package/src/components/{layout → atoms}/DXBox/DXBox.vue +69 -2
  14. package/src/components/atoms/DXButton/DXButton.stories.js +94 -1
  15. package/src/components/atoms/DXButton/DXButton.vue +145 -11
  16. package/src/components/atoms/DXCard/DXCard.stories.js +72 -14
  17. package/src/components/atoms/DXCard/DXCard.vue +3 -4
  18. package/src/components/atoms/DXCheckbox/DXCheckbox.stories.js +85 -1
  19. package/src/components/atoms/DXCode/DXCode.stories.js +95 -0
  20. package/src/components/{layout → atoms}/DXContainer/DXContainer.stories.js +2 -2
  21. package/src/components/{layout → atoms}/DXContainer/DXContainer.vue +28 -1
  22. package/src/components/atoms/DXDivider/DXDivider.stories.js +84 -1
  23. package/src/components/{layout → atoms}/DXFlex/DXFlex.stories.js +2 -2
  24. package/src/components/{layout → atoms}/DXFlex/DXFlex.vue +57 -3
  25. package/src/components/atoms/DXFormLabel/DXFormLabel.stories.js +70 -1
  26. package/src/components/{layout → atoms}/DXGrid/DXGrid.stories.js +2 -2
  27. package/src/components/atoms/DXHeading/DXHeading.stories.js +143 -0
  28. package/src/components/atoms/DXIcon/DXIcon.stories.js +74 -0
  29. package/src/components/atoms/DXIconWrapper/DXIconWrapper.stories.js +69 -0
  30. package/src/components/atoms/DXImage/DXImage.stories.js +483 -0
  31. package/src/components/atoms/DXImage/DXImage.vue +294 -0
  32. package/src/components/atoms/DXImage/index.js +2 -0
  33. package/src/components/atoms/DXInputAddon/DXInputAddon.stories.js +86 -1
  34. package/src/components/atoms/DXLabel/DXLabel.stories.js +101 -0
  35. package/src/components/atoms/DXLink/DXLink.stories.js +75 -10
  36. package/src/components/atoms/DXLink/DXLink.vue +59 -3
  37. package/src/components/atoms/DXList/DXList.stories.js +125 -0
  38. package/src/components/atoms/DXLoader/DXLoader.stories.js +75 -1
  39. package/src/components/atoms/DXNav/DXNav.stories.js +236 -0
  40. package/src/components/atoms/DXNav/DXNav.vue +114 -0
  41. package/src/components/atoms/DXNav/index.js +2 -0
  42. package/src/components/atoms/DXProgress/DXProgress.stories.js +76 -1
  43. package/src/components/atoms/DXRadio/DXRadio.stories.js +85 -1
  44. package/src/components/atoms/DXSkeleton/DXSkeleton.stories.js +59 -1
  45. package/src/components/atoms/DXSlider/DXSlider.stories.js +89 -0
  46. package/src/components/{layout → atoms}/DXSpacer/DXSpacer.stories.js +2 -2
  47. package/src/components/{layout → atoms}/DXStack/DXStack.stories.js +2 -2
  48. package/src/components/{layout → atoms}/DXStack/DXStack.vue +5 -2
  49. package/src/components/atoms/DXTags/DXTags.stories.js +77 -0
  50. package/src/components/atoms/DXText/DXText.stories.js +129 -0
  51. package/src/components/atoms/DXToast/DXToast.stories.js +64 -1
  52. package/src/components/atoms/DXToggle/DXToggle.stories.js +84 -1
  53. package/src/components/atoms/DXToggleButton/DXToggleButton.stories.js +78 -1
  54. package/src/components/atoms/DXTooltip/DXTooltip.stories.js +98 -1
  55. package/src/components/atoms/index.js +16 -1
  56. package/src/components/index.js +1 -7
  57. package/src/components/molecules/DXActionButtons/DXActionButtons.stories.js +280 -77
  58. package/src/components/molecules/DXActionButtons/DXActionButtons.vue +31 -31
  59. package/src/components/molecules/DXAlert/DXAlert.stories.js +199 -1
  60. package/src/components/molecules/DXAlert/DXAlert.vue +35 -13
  61. package/src/components/molecules/DXBreadcrumb/DXBreadcrumb.stories.js +125 -1
  62. package/src/components/molecules/DXBreadcrumb/DXBreadcrumb.vue +22 -18
  63. package/src/components/molecules/DXButtonGroup/DXButtonGroup.stories.js +193 -6
  64. package/src/components/molecules/DXButtonGroup/DXButtonGroup.vue +39 -3
  65. package/src/components/molecules/DXCloseButton/DXCloseButton.stories.js +64 -1
  66. package/src/components/molecules/DXComboBox/DXComboBox.stories.js +66 -0
  67. package/src/components/molecules/DXCopyField/DXCopyField.stories.js +128 -1
  68. package/src/components/molecules/DXCopyField/DXCopyField.vue +60 -7
  69. package/src/components/molecules/DXDataFilter/DXDataFilter.vue +8 -6
  70. package/src/components/molecules/DXDatePicker/DXDatePicker.stories.js +58 -0
  71. package/src/components/molecules/DXFileUpload/DXFileUpload.stories.js +66 -0
  72. package/src/components/molecules/DXFilterGroup/DXFilterGroup.stories.js +61 -0
  73. package/src/components/molecules/DXFormControl/DXFormControl.stories.js +76 -0
  74. package/src/components/molecules/DXFormControl/DXFormControl.vue +9 -8
  75. package/src/components/molecules/DXInput/DXInput.stories.js +100 -1
  76. package/src/components/molecules/DXInputGroup/DXInputGroup.stories.js +89 -1
  77. package/src/components/molecules/DXInputMask/DXInputMask.stories.js +67 -0
  78. package/src/components/molecules/DXMenu/DXMenu.stories.js +111 -4
  79. package/src/components/molecules/DXMenu/DXMenu.vue +18 -5
  80. package/src/components/molecules/DXMenu/README.md +1 -1
  81. package/src/components/molecules/DXPagination/DXPagination.stories.js +105 -2
  82. package/src/components/molecules/DXPagination/DXPagination.vue +18 -33
  83. package/src/components/molecules/DXPasswordInput/DXPasswordInput.stories.js +67 -1
  84. package/src/components/molecules/DXRadioCard/DXRadioCard.stories.js +64 -0
  85. package/src/components/molecules/DXRadioGroup/DXRadioGroup.stories.js +84 -0
  86. package/src/components/molecules/DXRating/DXRating.stories.js +3 -2
  87. package/src/components/molecules/DXSearchBar/DXSearchBar.stories.js +1 -1
  88. package/src/components/molecules/DXSearchBar/DXSearchBar.vue +16 -12
  89. package/src/components/molecules/DXSearchSelect/DXSearchSelect.stories.js +71 -0
  90. package/src/components/molecules/DXSegmentedControl/DXSegmentedControl.stories.js +74 -0
  91. package/src/components/molecules/DXSelect/DXSelect.stories.js +92 -1
  92. package/src/components/molecules/DXStatCard/DXStatCard.stories.js +1 -1
  93. package/src/components/molecules/DXStatCard/DXStatCard.vue +30 -26
  94. package/src/components/molecules/DXTableFiltersPanel/index.js +2 -0
  95. package/src/components/molecules/DXTablePagination/DXTablePagination.stories.js +67 -0
  96. package/src/components/molecules/DXTableToolbar/DXTableToolbar.stories.js +71 -0
  97. package/src/components/molecules/DXTextarea/DXTextarea.stories.js +87 -1
  98. package/src/components/molecules/DXTimePicker/DXTimePicker.stories.js +1 -1
  99. package/src/components/molecules/DXValidationIcon/DXValidationIcon.stories.js +59 -1
  100. package/src/components/molecules/index.js +0 -1
  101. package/src/components/organisms/DXAccordion/DXAccordion.stories.js +75 -0
  102. package/src/components/organisms/DXAppLayout/DXAppLayout.stories.js +28 -26
  103. package/src/components/organisms/DXAuthenticationForm/DXAuthenticationForm.stories.js +0 -2
  104. package/src/components/organisms/DXAuthenticationForm/DXAuthenticationForm.vue +5 -8
  105. package/src/components/{molecules → organisms}/DXBaseTable/DXBaseTable.stories.js +78 -1
  106. package/src/components/{molecules → organisms}/DXBaseTable/DXBaseTable.vue +2 -2
  107. package/src/components/organisms/DXChartContainer/DXChartContainer.stories.js +1 -1
  108. package/src/components/organisms/DXChartContainer/DXChartContainer.vue +10 -6
  109. package/src/components/organisms/DXChatInterface/DXChatInterface.stories.js +1 -1
  110. package/src/components/organisms/DXChatInterface/DXChatInterface.vue +6 -4
  111. package/src/components/organisms/DXCommentSection/DXCommentSection.stories.js +1 -1
  112. package/src/components/organisms/DXCommentSection/DXCommentSection.vue +7 -6
  113. package/src/components/organisms/DXDashboardGrid/DXDashboardGrid.stories.js +1 -1
  114. package/src/components/organisms/DXDashboardGrid/DXDashboardGrid.vue +4 -2
  115. package/src/components/organisms/DXDashboardWidget/DXDashboardWidget.stories.js +1 -1
  116. package/src/components/organisms/DXDashboardWidget/DXDashboardWidget.vue +3 -2
  117. package/src/components/organisms/DXDataTable/DXDataTable.stories.js +1 -1
  118. package/src/components/organisms/DXDropdown/DXDropdown.stories.js +84 -1
  119. package/src/components/organisms/DXEmptyState/DXEmptyState.stories.js +64 -0
  120. package/src/components/organisms/DXEmptyState/DXEmptyState.vue +4 -2
  121. package/src/components/organisms/DXHeaderBar/DXHeaderBar.stories.js +409 -3
  122. package/src/components/organisms/DXHeaderBar/DXHeaderBar.vue +262 -53
  123. package/src/components/organisms/DXMediaGallery/DXMediaGallery.stories.js +1 -1
  124. package/src/components/organisms/DXMediaGallery/DXMediaGallery.vue +7 -5
  125. package/src/components/organisms/DXModal/DXModal.stories.js +93 -1
  126. package/src/components/organisms/DXModal/DXModal.vue +3 -2
  127. package/src/components/organisms/DXNotificationCenter/DXNotificationCenter.stories.js +1 -1
  128. package/src/components/organisms/DXNotificationCenter/DXNotificationCenter.vue +2 -1
  129. package/src/components/organisms/DXReportGenerator/DXReportGenerator.vue +4 -3
  130. package/src/components/organisms/DXSettingsPanel/DXSettingsPanel.vue +11 -8
  131. package/src/components/organisms/DXSidebar/DXSidebar.stories.js +1 -1
  132. package/src/components/organisms/DXSidebarMenu/DXSidebarMenu.stories.js +104 -1
  133. package/src/components/organisms/DXSidebarMenu/DXSidebarMenu.vue +14 -4
  134. package/src/components/organisms/DXSidebarMenu/README.md +3 -3
  135. package/src/components/organisms/DXTable/DXTable.stories.js +138 -11
  136. package/src/components/organisms/DXTable/DXTable.vue +1 -1
  137. package/src/components/organisms/DXTabs/DXTabs.stories.js +91 -1
  138. package/src/components/organisms/DXUserProfileCard/DXUserProfileCard.stories.js +1 -1
  139. package/src/components/organisms/DXUserProfileCard/DXUserProfileCard.vue +20 -18
  140. package/src/components/organisms/index.js +1 -0
  141. package/src/components/utilities/DXAnimatePresence/DXAnimatePresence.stories.js +1 -1
  142. package/src/components/utilities/DXBreakpointProvider/DXBreakpointProvider.stories.js +2 -2
  143. package/src/components/utilities/DXObserver/DXObserver.stories.js +1 -1
  144. package/src/components/utilities/DXPortal/DXPortal.stories.js +1 -1
  145. package/src/components/utilities/DXStaggeredAnimation/DXStaggeredAnimation.stories.js +2 -2
  146. package/src/components/utilities/DXThemeProvider/DXThemeProvider.stories.js +1 -1
  147. package/src/components/utilities/DXTransitionGroup/DXTransitionGroup.stories.js +1 -1
  148. package/src/composables/useSize.js +8 -1
  149. package/src/index.js +1 -7
  150. package/src/components/layout/index.js +0 -8
  151. package/src/components/typography/DXBlockquote/DXBlockquote.stories.js +0 -33
  152. package/src/components/typography/DXCode/DXCode.stories.js +0 -29
  153. package/src/components/typography/DXHeading/DXHeading.stories.js +0 -54
  154. package/src/components/typography/DXLabel/DXLabel.stories.js +0 -40
  155. package/src/components/typography/DXList/DXList.stories.js +0 -50
  156. package/src/components/typography/DXText/DXText.stories.js +0 -47
  157. package/src/components/typography/index.js +0 -8
  158. /package/src/components/{typography → atoms}/DXBlockquote/DXBlockquote.vue +0 -0
  159. /package/src/components/{typography → atoms}/DXBlockquote/index.js +0 -0
  160. /package/src/components/{layout → atoms}/DXBox/index.js +0 -0
  161. /package/src/components/{typography → atoms}/DXCode/DXCode.vue +0 -0
  162. /package/src/components/{typography → atoms}/DXCode/index.js +0 -0
  163. /package/src/components/{layout → atoms}/DXContainer/index.js +0 -0
  164. /package/src/components/{layout → atoms}/DXFlex/index.js +0 -0
  165. /package/src/components/{layout → atoms}/DXGrid/DXGrid.vue +0 -0
  166. /package/src/components/{layout → atoms}/DXGrid/index.js +0 -0
  167. /package/src/components/{typography → atoms}/DXHeading/DXHeading.vue +0 -0
  168. /package/src/components/{typography → atoms}/DXHeading/index.js +0 -0
  169. /package/src/components/{typography → atoms}/DXLabel/DXLabel.vue +0 -0
  170. /package/src/components/{typography → atoms}/DXLabel/index.js +0 -0
  171. /package/src/components/{typography → atoms}/DXList/DXList.vue +0 -0
  172. /package/src/components/{typography → atoms}/DXList/index.js +0 -0
  173. /package/src/components/{layout → atoms}/DXSpacer/DXSpacer.vue +0 -0
  174. /package/src/components/{layout → atoms}/DXSpacer/index.js +0 -0
  175. /package/src/components/{layout → atoms}/DXStack/index.js +0 -0
  176. /package/src/components/{typography → atoms}/DXText/DXText.vue +0 -0
  177. /package/src/components/{typography → atoms}/DXText/index.js +0 -0
  178. /package/src/components/{molecules → organisms}/DXBaseTable/index.js +0 -0
@@ -12,12 +12,13 @@
12
12
  :data-variant="variant"
13
13
  :data-size="size"
14
14
  :data-disabled="disabled"
15
+ :data-inactive="inactive"
15
16
  :data-external="isExternal"
16
17
  @click="handleClick"
17
18
  >
18
19
  <slot />
19
20
  <DXIcon
20
- v-if="showExternalIcon && isExternal && !disabled"
21
+ v-if="showExternalIcon && isExternal && !disabled && !inactive"
21
22
  :icon="ArrowTopRightOnSquareIcon"
22
23
  :size="iconSize"
23
24
  class="ml-0.5"
@@ -49,6 +50,8 @@ const props = defineProps({
49
50
  size: { type: String, default: "md" },
50
51
  /** Отключенное состояние */
51
52
  disabled: { type: Boolean, default: false },
53
+ /** Неактивное состояние (визуально приглушенная, но кликабельная) */
54
+ inactive: { type: Boolean, default: false },
52
55
  /** Кастомный aria-label */
53
56
  ariaLabel: { type: String, default: null },
54
57
  /** Показывать иконку для внешних ссылок */
@@ -161,16 +164,69 @@ const textSizeClass = computed(() => {
161
164
  return useSize(props.size, "text");
162
165
  });
163
166
 
167
+ /**
168
+ * Классы padding для вариантов (кроме link)
169
+ */
170
+ const variantPaddingClass = computed(() => {
171
+ if (props.variant === "link") return "";
172
+
173
+ // Небольшие отступы в зависимости от размера
174
+ const paddingMap = {
175
+ xs: "px-1 py-0.5",
176
+ sm: "px-1.5 py-0.5",
177
+ md: "px-2 py-0.5",
178
+ lg: "px-2.5 py-1",
179
+ xl: "px-3 py-1",
180
+ };
181
+ return paddingMap[props.size] || paddingMap.md;
182
+ });
183
+
184
+ /**
185
+ * Классы для неактивного состояния
186
+ */
187
+ const inactiveVariantClass = computed(() => {
188
+ if (props.variant === "link") {
189
+ return "text-slate-600 no-underline cursor-default";
190
+ }
191
+
192
+ // Для остальных вариантов - базовые цвета без hover, с меньшей opacity для яркости
193
+ const baseColors = {
194
+ primary: "bg-slate-800 text-white",
195
+ secondary: "bg-slate-200 text-slate-700",
196
+ ghost: "text-slate-600 bg-transparent",
197
+ outline: "border border-slate-200 text-slate-600 bg-white",
198
+ success: "bg-emerald-600 text-white",
199
+ warning: "bg-amber-600 text-white",
200
+ danger: "bg-rose-600 text-white",
201
+ info: "bg-blue-600 text-white",
202
+ soft: "bg-slate-50 text-slate-600",
203
+ };
204
+
205
+ return useClassComposition(
206
+ baseColors[props.variant] || baseColors.primary,
207
+ variantPaddingClass.value,
208
+ "rounded-lg opacity-75"
209
+ );
210
+ });
211
+
164
212
  /**
165
213
  * Классы варианта стилизации
166
214
  */
167
215
  const variantClass = computed(() => {
216
+ if (props.inactive) {
217
+ return inactiveVariantClass.value;
218
+ }
219
+
168
220
  if (props.variant === "link") {
169
221
  // Кастомные классы для варианта link
170
222
  return "text-blue-600 hover:text-blue-700 underline-offset-2 hover:underline";
171
223
  }
172
- // Для остальных вариантов используем useVariantButton
173
- return useVariantButton(props.variant);
224
+ // Для остальных вариантов используем useVariantButton + padding + border-radius
225
+ return useClassComposition(
226
+ useVariantButton(props.variant),
227
+ variantPaddingClass.value,
228
+ "rounded-lg"
229
+ );
174
230
  });
175
231
 
176
232
  /**
@@ -0,0 +1,125 @@
1
+ import DXList from './DXList.vue';
2
+
3
+ export default {
4
+ title: 'Atoms/DXList',
5
+ component: DXList,
6
+ tags: ['autodocs', 'category:typography'],
7
+ parameters: {
8
+ docs: {
9
+ description: {
10
+ component: `
11
+ # DXList
12
+
13
+ Компонент списка с поддержкой упорядоченных и неупорядоченных списков, различных стилей маркеров и размеров.
14
+
15
+ ## Назначение
16
+
17
+ DXList предоставляет стандартизированный способ создания списков с настраиваемыми параметрами
18
+ типографики. Компонент автоматически рендерится как \`<ul>\` или \`<ol>\` для семантической правильности.
19
+
20
+ ## Архитектура
21
+
22
+ ### Использует
23
+ - Динамический рендеринг HTML тегов - для семантической правильности
24
+
25
+ ### Используется в
26
+ - Списки элементов
27
+ - Навигационные меню
28
+ - Оглавления
29
+ - Любые места, требующие структурированных списков
30
+
31
+ ## Внутренняя логика
32
+
33
+ ### Типы списков
34
+ - **unordered** (по умолчанию): Неупорядоченный список (\`<ul>\`)
35
+ - **ordered**: Упорядоченный список (\`<ol>\`) - при \`ordered={true}\`
36
+
37
+ ### Стили маркеров
38
+ Поддерживает различные стили маркеров:
39
+ - **disc** - заполненные круги (по умолчанию для ul)
40
+ - **circle** - пустые круги
41
+ - **square** - квадраты
42
+ - **decimal** - числа (по умолчанию для ol)
43
+ - **none** - без маркеров
44
+
45
+ ### Размеры
46
+ Поддерживает 3 размера текста:
47
+ - **sm** - маленький текст
48
+ - **md** - средний текст (по умолчанию)
49
+ - **lg** - большой текст
50
+
51
+ ### Отступы между элементами
52
+ Поддерживает 4 варианта отступов:
53
+ - **none** - без отступов
54
+ - **sm** - маленькие отступы (по умолчанию)
55
+ - **md** - средние отступы
56
+ - **lg** - большие отступы
57
+
58
+ ## Особенности
59
+
60
+ ### Семантика
61
+ Компонент использует семантически правильные HTML теги:
62
+ - \`<ul>\` для неупорядоченных списков
63
+ - \`<ol>\` для упорядоченных списков
64
+
65
+ Это важно для:
66
+ - SEO (поисковые системы понимают структуру)
67
+ - Доступности (скринридеры используют списки для навигации)
68
+ - Семантической правильности HTML
69
+
70
+ ### Элементы списка
71
+ Элементы списка должны быть обернуты в \`<li>\` теги через slot:
72
+ \`\`\`vue
73
+ <DXList>
74
+ <li>Первый элемент</li>
75
+ <li>Второй элемент</li>
76
+ </DXList>
77
+ \`\`\`
78
+ `,
79
+ },
80
+ },
81
+ },
82
+ };
83
+
84
+ export const Unordered = {
85
+ render: () => ({
86
+ components: { DXList },
87
+ template: `
88
+ <DXList>
89
+ <li>Первый элемент</li>
90
+ <li>Второй элемент</li>
91
+ <li>Третий элемент</li>
92
+ </DXList>
93
+ `,
94
+ }),
95
+ };
96
+
97
+ export const Ordered = {
98
+ args: { ordered: true },
99
+ render: (args) => ({
100
+ components: { DXList },
101
+ setup() { return { args }; },
102
+ template: `
103
+ <DXList v-bind="args">
104
+ <li>Шаг первый</li>
105
+ <li>Шаг второй</li>
106
+ <li>Шаг третий</li>
107
+ </DXList>
108
+ `,
109
+ }),
110
+ };
111
+
112
+ export const NoMarker = {
113
+ args: { marker: 'none' },
114
+ render: (args) => ({
115
+ components: { DXList },
116
+ setup() { return { args }; },
117
+ template: `
118
+ <DXList v-bind="args">
119
+ <li>Элемент без маркера</li>
120
+ <li>Ещё один элемент</li>
121
+ </DXList>
122
+ `,
123
+ }),
124
+ };
125
+
@@ -3,7 +3,81 @@ import DXLoader from './DXLoader.vue';
3
3
  export default {
4
4
  title: 'Atoms/DXLoader',
5
5
  component: DXLoader,
6
- tags: ['autodocs'],
6
+ tags: ['autodocs', 'category:feedback'],
7
+ parameters: {
8
+ docs: {
9
+ description: {
10
+ component: `
11
+ # DXLoader
12
+
13
+ Компонент индикатора загрузки с поддержкой различных размеров, цветов и текста.
14
+
15
+ ## Назначение
16
+
17
+ DXLoader предоставляет стандартизированный способ отображения состояния загрузки.
18
+ Компонент поддерживает различные размеры, цвета и возможность отображения текста рядом с индикатором.
19
+
20
+ ## Архитектура
21
+
22
+ ### Использует
23
+ - \`useSize\` composable - для унификации размеров (xs, sm, md, lg, xl)
24
+ - \`useVariantLoader\` composable - для цветовых вариантов
25
+
26
+ ### Используется в
27
+ - Загрузка данных
28
+ - Обработка запросов
29
+ - Состояния ожидания
30
+ - Любые места, требующие индикации загрузки
31
+
32
+ ## Внутренняя логика
33
+
34
+ ### Размеры
35
+ Поддерживает 5 размеров:
36
+ - **xs** - очень маленький (8×8px)
37
+ - **sm** - маленький
38
+ - **md** - средний (по умолчанию)
39
+ - **lg** - большой
40
+ - **xl** - очень большой
41
+
42
+ ### Цвета
43
+ Поддерживает 7 цветовых вариантов:
44
+ - **default** (по умолчанию) - стандартный цвет
45
+ - **primary** - основной цвет
46
+ - **success** - зеленый цвет
47
+ - **warning** - желтый цвет
48
+ - **danger** - красный цвет
49
+ - **info** - синий цвет
50
+ - **slate** - серый цвет
51
+
52
+ ### Текст
53
+ Поддерживает отображение текста рядом с индикатором:
54
+ - Через \`text\` prop
55
+ - Через default slot
56
+ - Управляется через \`showText\` prop
57
+
58
+ ## Особенности
59
+
60
+ ### Анимация
61
+ Использует CSS анимацию \`animate-spin\` для вращения индикатора.
62
+
63
+ ### Отображение текста
64
+ Логика отображения текста:
65
+ - Если \`showText\` явно указан → используется его значение
66
+ - Если есть default slot → текст показывается
67
+ - Если есть \`text\` prop → текст показывается
68
+ - Иначе → текст не показывается
69
+
70
+ ### Визуальное оформление
71
+ - Круглый индикатор с границей
72
+ - Вращающаяся анимация
73
+ - Цветная верхняя граница для визуального эффекта
74
+
75
+ ### Слоты
76
+ - **default** - кастомный текст (если нужно)
77
+ `,
78
+ },
79
+ },
80
+ },
7
81
  argTypes: {
8
82
  size: {
9
83
  control: 'select',
@@ -0,0 +1,236 @@
1
+ import DXNav from './DXNav.vue';
2
+ import DXLink from '../DXLink/DXLink.vue';
3
+ import DXButton from '../DXButton/DXButton.vue';
4
+
5
+ export default {
6
+ title: 'Atoms/DXNav',
7
+ component: DXNav,
8
+ tags: ['autodocs', 'category:navigation'],
9
+ parameters: {
10
+ docs: {
11
+ description: {
12
+ component: `
13
+ # DXNav
14
+
15
+ Универсальный компонент для обертки навигационных элементов с автоматическим управлением accessibility.
16
+
17
+ ## Назначение
18
+
19
+ DXNav - атомарный компонент для создания семантически правильных навигационных элементов.
20
+ Автоматически добавляет правильные \`aria-label\` атрибуты на основе типа навигации,
21
+ что улучшает accessibility и соответствие стандартам.
22
+
23
+ ## Архитектура
24
+
25
+ ### Используется в
26
+ - \`DXBreadcrumb\` - для хлебных крошек
27
+ - \`DXPagination\` - для пагинации
28
+ - \`DXMenu\` - для навигационных меню
29
+ - \`DXSidebarMenu\` - для боковых меню
30
+ - \`DXHeaderBar\` - для основной навигации в шапке
31
+ - Любые другие компоненты, требующие навигации
32
+
33
+ ## Внутренняя логика
34
+
35
+ ### Автоматические aria-label
36
+ Компонент автоматически добавляет \`aria-label\` на основе типа:
37
+ - \`breadcrumb\` → \`aria-label="Breadcrumb"\`
38
+ - \`pagination\` → \`aria-label="Pagination"\`
39
+ - \`menu\` → \`aria-label="Navigation menu"\`
40
+ - \`main\` → \`aria-label="Main navigation"\`
41
+ - \`custom\` → без автоматического label (требует \`ariaLabel\` prop)
42
+
43
+ ### Приоритет aria-label
44
+ 1. Если передан \`ariaLabel\` prop → используется он (переопределяет тип)
45
+ 2. Если \`type !== 'custom'\` → используется автоматический label
46
+ 3. Если \`type === 'custom'\` и нет \`ariaLabel\` → aria-label не добавляется
47
+
48
+ ### Передача атрибутов
49
+ Все атрибуты (кроме \`class\` и \`aria-label\`) автоматически передаются через \`$attrs\`:
50
+ - HTML атрибуты (id, data-*, и т.д.)
51
+ - События (@click, @input, и т.д.)
52
+
53
+ ## Особенности
54
+
55
+ ### Типы навигации
56
+ - **breadcrumb**: Для хлебных крошек (показывает путь к текущей странице)
57
+ - **pagination**: Для пагинации (навигация по страницам)
58
+ - **menu**: Для навигационных меню (боковые панели, выпадающие меню)
59
+ - **main**: Для основной навигации (главное меню сайта)
60
+ - **custom**: Для кастомных случаев (требует явного \`ariaLabel\`)
61
+
62
+ ### Accessibility
63
+ Компонент автоматически обеспечивает правильную семантику и accessibility:
64
+ - Семантический \`<nav>\` тег
65
+ - Правильные \`aria-label\` для screen readers
66
+ - Поддержка всех стандартных HTML атрибутов
67
+ - Совместимость с клавиатурной навигацией
68
+
69
+ ## Ограничения
70
+
71
+ - Для \`custom\` типа рекомендуется всегда указывать \`ariaLabel\` для accessibility
72
+ - Компонент не добавляет стили - используйте \`class\` prop для стилизации
73
+ - Все атрибуты передаются как есть (кроме \`class\` и \`aria-label\`)
74
+ `,
75
+ },
76
+ },
77
+ },
78
+ argTypes: {
79
+ type: {
80
+ control: { type: 'select' },
81
+ options: ['breadcrumb', 'pagination', 'menu', 'main', 'custom'],
82
+ description: 'Тип навигации',
83
+ table: {
84
+ type: { summary: 'string' },
85
+ defaultValue: { summary: 'main' },
86
+ },
87
+ },
88
+ ariaLabel: {
89
+ control: { type: 'text' },
90
+ description: 'Кастомный aria-label (переопределяет автоматический)',
91
+ table: {
92
+ type: { summary: 'string' },
93
+ defaultValue: { summary: 'null' },
94
+ },
95
+ },
96
+ class: {
97
+ control: { type: 'text' },
98
+ description: 'Дополнительные CSS классы',
99
+ table: {
100
+ type: { summary: 'string | array | object' },
101
+ defaultValue: { summary: 'null' },
102
+ },
103
+ },
104
+ },
105
+ };
106
+
107
+ export const Default = {
108
+ args: {
109
+ type: 'main',
110
+ },
111
+ render: (args) => ({
112
+ components: { DXNav, DXLink },
113
+ setup() {
114
+ return { args };
115
+ },
116
+ template: `
117
+ <DXNav v-bind="args" class="flex gap-4">
118
+ <DXLink href="/">Home</DXLink>
119
+ <DXLink href="/about">About</DXLink>
120
+ <DXLink href="/contact">Contact</DXLink>
121
+ </DXNav>
122
+ `,
123
+ }),
124
+ };
125
+
126
+ export const Breadcrumb = {
127
+ args: {
128
+ type: 'breadcrumb',
129
+ },
130
+ render: (args) => ({
131
+ components: { DXNav, DXLink },
132
+ setup() {
133
+ return { args };
134
+ },
135
+ template: `
136
+ <DXNav v-bind="args" class="flex items-center gap-2">
137
+ <DXLink href="/">Home</DXLink>
138
+ <span class="text-slate-400">/</span>
139
+ <DXLink href="/products">Products</DXLink>
140
+ <span class="text-slate-400">/</span>
141
+ <DXLink href="/products/current" inactive>Current Page</DXLink>
142
+ </DXNav>
143
+ `,
144
+ }),
145
+ };
146
+
147
+ export const Pagination = {
148
+ args: {
149
+ type: 'pagination',
150
+ },
151
+ render: (args) => ({
152
+ components: { DXNav, DXButton },
153
+ setup() {
154
+ return { args };
155
+ },
156
+ template: `
157
+ <DXNav v-bind="args" class="flex items-center gap-1">
158
+ <DXButton variant="ghost" size="sm">Previous</DXButton>
159
+ <DXButton variant="primary" size="sm">1</DXButton>
160
+ <DXButton variant="ghost" size="sm">2</DXButton>
161
+ <DXButton variant="ghost" size="sm">3</DXButton>
162
+ <DXButton variant="ghost" size="sm">Next</DXButton>
163
+ </DXNav>
164
+ `,
165
+ }),
166
+ };
167
+
168
+ export const Menu = {
169
+ args: {
170
+ type: 'menu',
171
+ },
172
+ render: (args) => ({
173
+ components: { DXNav, DXLink },
174
+ setup() {
175
+ return { args };
176
+ },
177
+ template: `
178
+ <DXNav v-bind="args" class="flex flex-col gap-1">
179
+ <DXLink href="/dashboard" variant="ghost">Dashboard</DXLink>
180
+ <DXLink href="/settings" variant="ghost">Settings</DXLink>
181
+ <DXLink href="/profile" variant="ghost">Profile</DXLink>
182
+ </DXNav>
183
+ `,
184
+ }),
185
+ };
186
+
187
+ export const CustomAriaLabel = {
188
+ args: {
189
+ type: 'custom',
190
+ ariaLabel: 'Site navigation',
191
+ },
192
+ render: (args) => ({
193
+ components: { DXNav, DXLink },
194
+ setup() {
195
+ return { args };
196
+ },
197
+ template: `
198
+ <DXNav v-bind="args" class="flex gap-4">
199
+ <DXLink href="/">Home</DXLink>
200
+ <DXLink href="/about">About</DXLink>
201
+ <DXLink href="/contact">Contact</DXLink>
202
+ </DXNav>
203
+ <p class="text-sm text-slate-600 mt-4">
204
+ Используется кастомный aria-label: "Site navigation"
205
+ </p>
206
+ `,
207
+ }),
208
+ };
209
+
210
+ export const WithAttributes = {
211
+ args: {
212
+ type: 'main',
213
+ },
214
+ render: (args) => ({
215
+ components: { DXNav, DXLink },
216
+ setup() {
217
+ return { args };
218
+ },
219
+ template: `
220
+ <DXNav
221
+ v-bind="args"
222
+ id="main-nav"
223
+ class="flex gap-4 p-4 bg-slate-50 rounded-lg"
224
+ data-testid="navigation"
225
+ >
226
+ <DXLink href="/">Home</DXLink>
227
+ <DXLink href="/about">About</DXLink>
228
+ <DXLink href="/contact">Contact</DXLink>
229
+ </DXNav>
230
+ <p class="text-sm text-slate-600 mt-4">
231
+ Все атрибуты (id, data-testid, class) передаются корректно
232
+ </p>
233
+ `,
234
+ }),
235
+ };
236
+
@@ -0,0 +1,114 @@
1
+ <template>
2
+ <nav
3
+ :aria-label="computedAriaLabel"
4
+ :class="computedClass"
5
+ data-component="DXNav"
6
+ :data-type="type"
7
+ v-bind="navAttrs"
8
+ >
9
+ <slot />
10
+ </nav>
11
+ </template>
12
+
13
+ <script setup>
14
+ import { computed, useAttrs } from "vue";
15
+
16
+ const props = defineProps({
17
+ /** Тип навигации: breadcrumb | pagination | menu | main | custom */
18
+ type: {
19
+ type: String,
20
+ default: "main",
21
+ validator: (value) => ["breadcrumb", "pagination", "menu", "main", "custom"].includes(value),
22
+ },
23
+ /** Кастомный aria-label (переопределяет автоматический label для типа) */
24
+ ariaLabel: {
25
+ type: String,
26
+ default: null,
27
+ },
28
+ /** Дополнительные CSS классы */
29
+ class: {
30
+ type: [String, Array, Object],
31
+ default: null,
32
+ },
33
+ });
34
+
35
+ const attrs = useAttrs();
36
+
37
+ /**
38
+ * Атрибуты для передачи на nav элемент (исключая class и aria-label, которые обрабатываются отдельно)
39
+ *
40
+ * @description
41
+ * В Vue 3 с <script setup>, props определенные через defineProps автоматически
42
+ * не попадают в $attrs. Исключаем class и aria-label, так как они обрабатываются отдельно.
43
+ */
44
+ const navAttrs = computed(() => {
45
+ const { class: _, "aria-label": __, ...rest } = attrs;
46
+ return rest;
47
+ });
48
+
49
+ /**
50
+ * Автоматические aria-label для типов навигации
51
+ *
52
+ * @description
53
+ * Маппинг типов навигации на стандартные aria-label значения
54
+ * для улучшения accessibility.
55
+ */
56
+ const typeAriaLabels = {
57
+ breadcrumb: "Breadcrumb",
58
+ pagination: "Pagination",
59
+ menu: "Navigation menu",
60
+ main: "Main navigation",
61
+ };
62
+
63
+ /**
64
+ * Вычисляет aria-label для nav элемента
65
+ *
66
+ * @description
67
+ * Приоритет:
68
+ * 1. Если передан ariaLabel prop → используем его
69
+ * 2. Если type !== 'custom' → используем автоматический label из typeAriaLabels
70
+ * 3. Если type === 'custom' и нет ariaLabel → возвращаем null (не добавляем aria-label)
71
+ *
72
+ * @returns {string|null} Значение aria-label или null
73
+ */
74
+ const computedAriaLabel = computed(() => {
75
+ // Если передан кастомный aria-label, используем его
76
+ if (props.ariaLabel) {
77
+ return props.ariaLabel;
78
+ }
79
+
80
+ // Если тип не custom, используем автоматический label
81
+ if (props.type !== "custom") {
82
+ return typeAriaLabels[props.type] || null;
83
+ }
84
+
85
+ // Для custom типа без ariaLabel не добавляем aria-label
86
+ return null;
87
+ });
88
+
89
+ /**
90
+ * Вычисляет классы для nav элемента
91
+ *
92
+ * @description
93
+ * Объединяет prop class с любыми классами из $attrs.class.
94
+ *
95
+ * @returns {string|Array|Object|null} Классы для применения к элементу
96
+ */
97
+ const computedClass = computed(() => {
98
+ const classes = [];
99
+
100
+ // Добавляем класс из prop class
101
+ if (props.class) {
102
+ classes.push(props.class);
103
+ }
104
+
105
+ // Добавляем класс из $attrs.class если есть
106
+ if (attrs.class) {
107
+ classes.push(attrs.class);
108
+ }
109
+
110
+ // Возвращаем массив если есть несколько классов, иначе одно значение или null
111
+ return classes.length > 1 ? classes : classes[0] || null;
112
+ });
113
+ </script>
114
+
@@ -0,0 +1,2 @@
1
+ export { default } from './DXNav.vue';
2
+