@dxtmisha/wiki 0.24.3 → 0.25.1
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/package.json +7 -7
- package/src/classes/WikiStorybookItem.ts +24 -0
- package/src/media/descriptions/wikiDescriptions.ts +28 -0
- package/src/media/descriptions/wikiDescriptionsAccordion.ts +158 -0
- package/src/media/descriptions/wikiDescriptionsActionSheet.ts +211 -0
- package/src/media/descriptions/wikiDescriptionsActions.ts +161 -0
- package/src/media/descriptions/wikiDescriptionsAnchor.ts +42 -0
- package/src/media/descriptions/wikiDescriptionsArrow.ts +181 -0
- package/src/media/descriptions/wikiDescriptionsBlock.ts +97 -0
- package/src/media/descriptions/wikiDescriptionsButton.ts +0 -1
- package/src/media/descriptions/wikiDescriptionsCell.ts +2 -1
- package/src/media/descriptions/wikiDescriptionsChipGroup.ts +168 -0
- package/src/media/descriptions/wikiDescriptionsDialog.ts +182 -0
- package/src/media/descriptions/wikiDescriptionsField.ts +2 -0
- package/src/media/descriptions/wikiDescriptionsImage.ts +41 -6
- package/src/media/descriptions/wikiDescriptionsInput.ts +257 -0
- package/src/media/descriptions/wikiDescriptionsMenu.ts +123 -0
- package/src/media/descriptions/wikiDescriptionsModal.ts +145 -0
- package/src/media/descriptions/wikiDescriptionsMotionTransform.ts +2 -6
- package/src/media/descriptions/wikiDescriptionsSelect.ts +209 -0
- package/src/media/descriptions/wikiDescriptionsSelectValue.ts +78 -0
- package/src/media/descriptions/wikiDescriptionsTextareaAutosize.ts +50 -0
- package/src/media/descriptions/wikiDescriptionsTooltip.ts +89 -0
- package/src/media/functional/en/conversions.mdx +67 -0
- package/src/media/functional/en/dataUtils.mdx +25 -0
- package/src/media/functional/en/datetimeRef.mdx +1 -1
- package/src/media/functional/en/eventRef.mdx +1 -1
- package/src/media/functional/en/executionUtils.mdx +58 -0
- package/src/media/functional/en/geoFlagRef.mdx +1 -1
- package/src/media/functional/en/geoIntlRef.mdx +1 -1
- package/src/media/functional/en/geoRef.mdx +2 -2
- package/src/media/functional/en/meta.mdx +1206 -0
- package/src/media/functional/en/metaManager.mdx +376 -0
- package/src/media/functional/en/metaOg.mdx +694 -0
- package/src/media/functional/en/metaTwitter.mdx +853 -0
- package/src/media/functional/en/reactive.mdx +40 -0
- package/src/media/functional/en/refTypes.mdx +1 -1
- package/src/media/functional/en/useApiRef.mdx +5 -5
- package/src/media/functional/en/useMeta.mdx +431 -0
- package/src/media/functional/en/validationUtils.mdx +11 -0
- package/src/media/functional/ru/conversions.mdx +67 -0
- package/src/media/functional/ru/dataUtils.mdx +25 -0
- package/src/media/functional/ru/datetimeRef.mdx +2 -2
- package/src/media/functional/ru/eventRef.mdx +1 -1
- package/src/media/functional/ru/executionUtils.mdx +58 -0
- package/src/media/functional/ru/geoFlagRef.mdx +1 -1
- package/src/media/functional/ru/geoIntl.mdx +2 -2
- package/src/media/functional/ru/geoIntlRef.mdx +1 -1
- package/src/media/functional/ru/geoRef.mdx +2 -2
- package/src/media/functional/ru/listTypes.mdx +1 -1
- package/src/media/functional/ru/meta.mdx +1330 -0
- package/src/media/functional/ru/metaManager.mdx +376 -0
- package/src/media/functional/ru/metaOg.mdx +694 -0
- package/src/media/functional/ru/metaTwitter.mdx +853 -0
- package/src/media/functional/ru/reactive.mdx +40 -0
- package/src/media/functional/ru/refTypes.mdx +2 -2
- package/src/media/functional/ru/useApiRef.mdx +5 -5
- package/src/media/functional/ru/useMeta.mdx +431 -0
- package/src/media/functional/ru/validationUtils.mdx +11 -0
- package/src/media/mdx/Accordion/accordion.en.mdx +59 -0
- package/src/media/mdx/Accordion/accordion.ru.mdx +59 -0
- package/src/media/mdx/Accordion/slots.en.mdx +6 -0
- package/src/media/mdx/Accordion/slots.ru.mdx +6 -0
- package/src/media/mdx/Accordion/wikiMdxAccordion.ts +25 -0
- package/src/media/mdx/ActionSheet/actionSheet.en.mdx +61 -0
- package/src/media/mdx/ActionSheet/actionSheet.ru.mdx +61 -0
- package/src/media/mdx/ActionSheet/touchClose.en.mdx +21 -0
- package/src/media/mdx/ActionSheet/touchClose.ru.mdx +21 -0
- package/src/media/mdx/ActionSheet/wikiMdxActionSheet.ts +25 -0
- package/src/media/mdx/Actions/actions.en.mdx +48 -0
- package/src/media/mdx/Actions/actions.ru.mdx +48 -0
- package/src/media/mdx/Actions/flexible.en.mdx +19 -0
- package/src/media/mdx/Actions/flexible.ru.mdx +19 -0
- package/src/media/mdx/Actions/list.en.mdx +50 -0
- package/src/media/mdx/Actions/list.ru.mdx +50 -0
- package/src/media/mdx/Actions/wikiMdxActions.ts +31 -0
- package/src/media/mdx/Anchor/anchor.en.mdx +34 -0
- package/src/media/mdx/Anchor/anchor.ru.mdx +34 -0
- package/src/media/mdx/Anchor/expose.go.en.mdx +6 -0
- package/src/media/mdx/Anchor/expose.go.ru.mdx +6 -0
- package/src/media/mdx/Anchor/hide.en.mdx +28 -0
- package/src/media/mdx/Anchor/hide.ru.mdx +28 -0
- package/src/media/mdx/Anchor/isCopy.en.mdx +23 -0
- package/src/media/mdx/Anchor/isCopy.ru.mdx +23 -0
- package/src/media/mdx/Anchor/scroll.en.mdx +34 -0
- package/src/media/mdx/Anchor/scroll.ru.mdx +35 -0
- package/src/media/mdx/Anchor/wikiMdxAnchor.ts +43 -0
- package/src/media/mdx/Arrow/arrow.en.mdx +33 -0
- package/src/media/mdx/Arrow/arrow.ru.mdx +33 -0
- package/src/media/mdx/Arrow/wikiMdxArrow.ts +19 -0
- package/src/media/mdx/Block/block.en.mdx +42 -0
- package/src/media/mdx/Block/block.ru.mdx +42 -0
- package/src/media/mdx/Block/wikiMdxBlock.ts +19 -0
- package/src/media/mdx/ChipGroup/chipGroup.en.mdx +51 -0
- package/src/media/mdx/ChipGroup/chipGroup.ru.mdx +51 -0
- package/src/media/mdx/ChipGroup/selected.en.mdx +50 -0
- package/src/media/mdx/ChipGroup/selected.ru.mdx +50 -0
- package/src/media/mdx/ChipGroup/wikiMdxChipGroup.ts +25 -0
- package/src/media/mdx/Dialog/buttons.en.mdx +45 -0
- package/src/media/mdx/Dialog/buttons.ru.mdx +45 -0
- package/src/media/mdx/Dialog/dialog.en.mdx +66 -0
- package/src/media/mdx/Dialog/dialog.ru.mdx +65 -0
- package/src/media/mdx/Dialog/events.en.mdx +63 -0
- package/src/media/mdx/Dialog/events.ru.mdx +63 -0
- package/src/media/mdx/Dialog/states.en.mdx +58 -0
- package/src/media/mdx/Dialog/states.ru.mdx +57 -0
- package/src/media/mdx/Dialog/wikiMdxDialog.ts +37 -0
- package/src/media/mdx/Field/arrows.en.mdx +22 -6
- package/src/media/mdx/Field/arrows.ru.mdx +22 -6
- package/src/media/mdx/Field/slots.en.mdx +0 -13
- package/src/media/mdx/Field/slots.ru.mdx +0 -13
- package/src/media/mdx/Image/img-tag.en.mdx +105 -0
- package/src/media/mdx/Image/img-tag.ru.mdx +105 -0
- package/src/media/mdx/Image/wikiMdxImage.ts +6 -0
- package/src/media/mdx/Input/currency.en.mdx +38 -0
- package/src/media/mdx/Input/currency.ru.mdx +38 -0
- package/src/media/mdx/Input/date.en.mdx +53 -0
- package/src/media/mdx/Input/date.ru.mdx +53 -0
- package/src/media/mdx/Input/input.en.mdx +143 -0
- package/src/media/mdx/Input/input.ru.mdx +71 -0
- package/src/media/mdx/Input/mask.en.mdx +30 -0
- package/src/media/mdx/Input/mask.ru.mdx +30 -0
- package/src/media/mdx/Input/number.en.mdx +41 -0
- package/src/media/mdx/Input/number.ru.mdx +41 -0
- package/src/media/mdx/Input/type.en.mdx +26 -0
- package/src/media/mdx/Input/type.ru.mdx +26 -0
- package/src/media/mdx/Input/wikiMdxInput.ts +49 -0
- package/src/media/mdx/Menu/event.updateValue.en.mdx +29 -0
- package/src/media/mdx/Menu/event.updateValue.ru.mdx +30 -0
- package/src/media/mdx/Menu/expose.navigation.en.mdx +12 -0
- package/src/media/mdx/Menu/expose.navigation.ru.mdx +12 -0
- package/src/media/mdx/Menu/navigation.en.mdx +56 -0
- package/src/media/mdx/Menu/navigation.ru.mdx +56 -0
- package/src/media/mdx/Menu/slots.control.en.mdx +65 -0
- package/src/media/mdx/Menu/slots.control.ru.mdx +65 -0
- package/src/media/mdx/Menu/slots.en.mdx +2 -24
- package/src/media/mdx/Menu/slots.ru.mdx +2 -24
- package/src/media/mdx/Menu/wikiMdxMenu.ts +27 -3
- package/src/media/mdx/Modal/differences.en.mdx +130 -0
- package/src/media/mdx/Modal/differences.ru.mdx +65 -0
- package/src/media/mdx/Modal/modal.en.mdx +63 -0
- package/src/media/mdx/Modal/modal.ru.mdx +63 -0
- package/src/media/mdx/Modal/wikiMdxModal.ts +25 -0
- package/src/media/mdx/MotionTransform/expose.motionTransformElement.en.mdx +13 -0
- package/src/media/mdx/MotionTransform/expose.motionTransformElement.ru.mdx +14 -0
- package/src/media/mdx/MotionTransform/wikiMdxMotionTransform.ts +6 -0
- package/src/media/mdx/Select/select.en.mdx +69 -0
- package/src/media/mdx/Select/select.ru.mdx +69 -0
- package/src/media/mdx/Select/wikiMdxSelect.ts +19 -0
- package/src/media/mdx/SelectValue/selectValue.en.mdx +64 -0
- package/src/media/mdx/SelectValue/selectValue.ru.mdx +64 -0
- package/src/media/mdx/SelectValue/wikiMdxSelectValue.ts +19 -0
- package/src/media/mdx/TextareaAutosize/textarea-autosize.en.mdx +65 -0
- package/src/media/mdx/TextareaAutosize/textarea-autosize.ru.mdx +65 -0
- package/src/media/mdx/TextareaAutosize/wikiMdxTextareaAutosize.ts +19 -0
- package/src/media/mdx/Tooltip/event.tooltip.en.mdx +7 -0
- package/src/media/mdx/Tooltip/event.tooltip.ru.mdx +8 -0
- package/src/media/mdx/Tooltip/slot.control.en.mdx +14 -0
- package/src/media/mdx/Tooltip/slot.control.ru.mdx +14 -0
- package/src/media/mdx/Tooltip/tooltip.en.mdx +34 -0
- package/src/media/mdx/Tooltip/tooltip.ru.mdx +34 -0
- package/src/media/mdx/Tooltip/wikiMdxTooltip.ts +31 -0
- package/src/media/mdx/Window/classes.ru.mdx +1 -1
- package/src/media/mdx/event/events.actions.en.mdx +44 -0
- package/src/media/mdx/event/events.actions.ru.mdx +44 -0
- package/src/media/mdx/event/events.inputStandard.en.mdx +6 -0
- package/src/media/mdx/event/events.inputStandard.ru.mdx +6 -0
- package/src/media/mdx/event/wikiMdxEvent.ts +20 -8
- package/src/media/mdx/expose/expose.descriptionId.en.mdx +6 -0
- package/src/media/mdx/expose/expose.descriptionId.ru.mdx +6 -0
- package/src/media/mdx/expose/expose.id.en.mdx +6 -0
- package/src/media/mdx/expose/expose.id.ru.mdx +6 -0
- package/src/media/mdx/expose/expose.labelId.en.mdx +6 -0
- package/src/media/mdx/expose/expose.labelId.ru.mdx +6 -0
- package/src/media/mdx/expose/wikiMdxExpose.ts +18 -0
- package/src/media/mdx/slot/body.en.mdx +6 -0
- package/src/media/mdx/slot/body.ru.mdx +6 -0
- package/src/media/mdx/slot/headline.en.mdx +7 -0
- package/src/media/mdx/slot/headline.ru.mdx +6 -0
- package/src/media/mdx/slot/leading.en.mdx +7 -0
- package/src/media/mdx/slot/leading.ru.mdx +7 -0
- package/src/media/mdx/slot/secondary.en.mdx +5 -0
- package/src/media/mdx/slot/secondary.ru.mdx +5 -0
- package/src/media/mdx/slot/trailing.en.mdx +7 -0
- package/src/media/mdx/slot/trailing.ru.mdx +7 -0
- package/src/media/mdx/slot/wikiMdxSlot.ts +30 -0
- package/src/media/mdx/style/isSkeleton.en.mdx +2 -2
- package/src/media/mdx/style/isSkeleton.ru.mdx +2 -2
- package/src/media/mdx/value/v-model-selected.en.mdx +28 -0
- package/src/media/mdx/value/v-model-selected.ru.mdx +28 -0
- package/src/media/mdx/value/v-model.en.mdx +26 -0
- package/src/media/mdx/value/v-model.ru.mdx +26 -0
- package/src/media/mdx/value/wikiMdxValue.ts +24 -3
- package/src/media/mdx/wikiMdx.ts +29 -1
- package/src/media/props/wiki.ts +42 -0
- package/src/media/props/wikiActions.ts +43 -0
- package/src/media/props/wikiActionsInclude.ts +62 -0
- package/src/media/props/wikiAnchor.ts +84 -0
- package/src/media/props/wikiAria.ts +102 -0
- package/src/media/props/wikiArrow.ts +24 -0
- package/src/media/props/wikiArrowInclude.ts +45 -0
- package/src/media/props/wikiBarsInclude.ts +80 -0
- package/src/media/props/wikiChipGroup.ts +39 -0
- package/src/media/props/wikiDialog.ts +34 -0
- package/src/media/props/wikiField.ts +0 -21
- package/src/media/props/wikiFieldCounterInclude.ts +78 -0
- package/src/media/props/wikiForm.ts +248 -0
- package/src/media/props/wikiHook.ts +20 -0
- package/src/media/props/wikiIcon.ts +3 -3
- package/src/media/props/wikiIconInclude.ts +319 -0
- package/src/media/props/wikiImage.ts +71 -19
- package/src/media/props/wikiInformation.ts +160 -0
- package/src/media/props/wikiInput.ts +34 -0
- package/src/media/props/wikiListItem.ts +20 -0
- package/src/media/props/wikiMask.ts +0 -10
- package/src/media/props/wikiMaskInclude.ts +54 -0
- package/src/media/props/wikiMenu.ts +0 -10
- package/src/media/props/wikiMotionTransform.ts +0 -10
- package/src/media/props/wikiOption.ts +113 -0
- package/src/media/props/wikiSelect.ts +68 -0
- package/src/media/props/wikiSelectValue.ts +30 -0
- package/src/media/props/wikiStatus.ts +29 -41
- package/src/media/props/wikiStyle.ts +154 -243
- package/src/media/props/wikiTechnical.ts +65 -0
- package/src/media/props/wikiText.ts +57 -0
- package/src/media/props/wikiTooltip.ts +53 -0
- package/src/media/props/wikiValue.ts +14 -203
- package/src/media/props/wikiWindow.ts +0 -31
- package/src/styles/storybookStyle.scss +3 -1
- package/src/types/storybookTypes.ts +26 -4
|
@@ -0,0 +1,853 @@
|
|
|
1
|
+
import {Meta} from '@storybook/addon-docs/blocks'
|
|
2
|
+
|
|
3
|
+
<Meta title='@dxtmisha/functional/ru/Classes/MetaTwitter'/>
|
|
4
|
+
|
|
5
|
+
# Класс MetaTwitter
|
|
6
|
+
|
|
7
|
+
Класс для работы с Twitter Card мета-тегами, обеспечивающий красивое отображение контента при публикации ссылок в Twitter (X). Наследует функциональность `MetaManager` и предоставляет типобезопасные методы для управления стандартными Twitter Card тегами.
|
|
8
|
+
|
|
9
|
+
## Основные возможности
|
|
10
|
+
|
|
11
|
+
- **Типобезопасность** — строгая типизация для всех Twitter Card тегов и типов карточек
|
|
12
|
+
- **Автоматическая синхронизация** — обновление мета-тегов в `<head>` документа
|
|
13
|
+
- **Поддержка всех типов карточек** — summary, summary_large_image, app, player
|
|
14
|
+
- **Удобный API** — специализированные методы для каждого тега
|
|
15
|
+
- **SSR совместимость** — генерация HTML для серверного рендеринга
|
|
16
|
+
- **Валидация типов** — поддержка всех официальных типов Twitter Cards
|
|
17
|
+
- **Цепочечные методы** — возможность последовательной установки значений
|
|
18
|
+
|
|
19
|
+
## Конструктор
|
|
20
|
+
|
|
21
|
+
### `constructor`
|
|
22
|
+
|
|
23
|
+
Создает экземпляр MetaTwitter с предустановленным списком всех поддерживаемых Twitter Card тегов. Автоматически считывает существующие теги из DOM.
|
|
24
|
+
|
|
25
|
+
```javascript
|
|
26
|
+
import { MetaTwitter } from '@dxtmisha/functional'
|
|
27
|
+
|
|
28
|
+
// Создание экземпляра
|
|
29
|
+
const twitter = new MetaTwitter()
|
|
30
|
+
|
|
31
|
+
// Класс автоматически управляет всеми стандартными Twitter Card тегами:
|
|
32
|
+
// twitter:card, twitter:site, twitter:creator, twitter:title,
|
|
33
|
+
// twitter:description, twitter:image и другими
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Методы получения данных
|
|
37
|
+
|
|
38
|
+
### `getCard`
|
|
39
|
+
|
|
40
|
+
Получает тип Twitter Card.
|
|
41
|
+
|
|
42
|
+
**Возвращает:** `MetaTwitterCard` — тип карточки
|
|
43
|
+
|
|
44
|
+
```javascript
|
|
45
|
+
const twitter = new MetaTwitter()
|
|
46
|
+
|
|
47
|
+
const cardType = twitter.getCard()
|
|
48
|
+
// 'summary_large_image'
|
|
49
|
+
|
|
50
|
+
// Проверка типа карточки
|
|
51
|
+
if (twitter.getCard() === 'summary_large_image') {
|
|
52
|
+
console.log('Карточка с большим изображением')
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Условная логика
|
|
56
|
+
switch (twitter.getCard()) {
|
|
57
|
+
case 'summary':
|
|
58
|
+
console.log('Обычная карточка')
|
|
59
|
+
break
|
|
60
|
+
case 'summary_large_image':
|
|
61
|
+
console.log('Карточка с большим изображением')
|
|
62
|
+
break
|
|
63
|
+
case 'player':
|
|
64
|
+
console.log('Видео плеер')
|
|
65
|
+
break
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### `getSite`
|
|
70
|
+
|
|
71
|
+
Получает Twitter аккаунт сайта или бренда.
|
|
72
|
+
|
|
73
|
+
**Возвращает:** `string` — @username сайта
|
|
74
|
+
|
|
75
|
+
```javascript
|
|
76
|
+
const twitter = new MetaTwitter()
|
|
77
|
+
|
|
78
|
+
const site = twitter.getSite()
|
|
79
|
+
// '@mywebsite'
|
|
80
|
+
|
|
81
|
+
// Использование для атрибуции
|
|
82
|
+
console.log('Сайт принадлежит:', twitter.getSite())
|
|
83
|
+
|
|
84
|
+
// Проверка наличия
|
|
85
|
+
if (twitter.getSite()) {
|
|
86
|
+
console.log('Twitter аккаунт сайта установлен')
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Извлечение имени без @
|
|
90
|
+
const username = twitter.getSite().replace('@', '')
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### `getCreator`
|
|
94
|
+
|
|
95
|
+
Получает Twitter аккаунт автора контента.
|
|
96
|
+
|
|
97
|
+
**Возвращает:** `string` — @username автора
|
|
98
|
+
|
|
99
|
+
```javascript
|
|
100
|
+
const twitter = new MetaTwitter()
|
|
101
|
+
|
|
102
|
+
const creator = twitter.getCreator()
|
|
103
|
+
// '@john_doe'
|
|
104
|
+
|
|
105
|
+
// Отображение автора
|
|
106
|
+
console.log('Автор:', twitter.getCreator())
|
|
107
|
+
|
|
108
|
+
// Использование в UI
|
|
109
|
+
const authorLink = `https://twitter.com/${twitter.getCreator().replace('@', '')}`
|
|
110
|
+
|
|
111
|
+
// Проверка авторства
|
|
112
|
+
if (twitter.getCreator()) {
|
|
113
|
+
console.log('У контента есть автор')
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### `getUrl`
|
|
118
|
+
|
|
119
|
+
Получает URL страницы для Twitter Card.
|
|
120
|
+
|
|
121
|
+
**Возвращает:** `string` — URL страницы
|
|
122
|
+
|
|
123
|
+
```javascript
|
|
124
|
+
const twitter = new MetaTwitter()
|
|
125
|
+
|
|
126
|
+
const url = twitter.getUrl()
|
|
127
|
+
// 'https://example.com/article/my-post'
|
|
128
|
+
|
|
129
|
+
// Получение домена
|
|
130
|
+
const domain = new URL(twitter.getUrl()).hostname
|
|
131
|
+
|
|
132
|
+
// Проверка совпадения с текущим URL
|
|
133
|
+
if (twitter.getUrl() !== window.location.href) {
|
|
134
|
+
console.warn('Twitter URL не совпадает с текущим')
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Создание ссылки для шаринга
|
|
138
|
+
const shareUrl = `https://twitter.com/intent/tweet?url=${encodeURIComponent(twitter.getUrl())}`
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### `getTitle`
|
|
142
|
+
|
|
143
|
+
Получает заголовок карточки.
|
|
144
|
+
|
|
145
|
+
**Возвращает:** `string` — заголовок карточки
|
|
146
|
+
|
|
147
|
+
```javascript
|
|
148
|
+
const twitter = new MetaTwitter()
|
|
149
|
+
|
|
150
|
+
const title = twitter.getTitle()
|
|
151
|
+
// 'Удивительная статья о веб-разработке'
|
|
152
|
+
|
|
153
|
+
// Использование для отладки
|
|
154
|
+
console.log('Twitter Title:', twitter.getTitle())
|
|
155
|
+
|
|
156
|
+
// Проверка длины (рекомендуется до 70 символов)
|
|
157
|
+
if (twitter.getTitle().length > 70) {
|
|
158
|
+
console.warn('Заголовок слишком длинный для Twitter')
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Использование в превью
|
|
162
|
+
const previewTitle = twitter.getTitle()
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### `getDescription`
|
|
166
|
+
|
|
167
|
+
Получает описание карточки.
|
|
168
|
+
|
|
169
|
+
**Возвращает:** `string` — описание карточки
|
|
170
|
+
|
|
171
|
+
```javascript
|
|
172
|
+
const twitter = new MetaTwitter()
|
|
173
|
+
|
|
174
|
+
const description = twitter.getDescription()
|
|
175
|
+
// 'Подробное руководство по созданию...'
|
|
176
|
+
|
|
177
|
+
// Проверка длины (рекомендуется до 200 символов)
|
|
178
|
+
if (twitter.getDescription().length > 200) {
|
|
179
|
+
console.warn('Описание слишком длинное')
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Обрезка для превью
|
|
183
|
+
const shortDesc = twitter.getDescription().substring(0, 150) + '...'
|
|
184
|
+
|
|
185
|
+
// Использование в мета-данных
|
|
186
|
+
console.log('Twitter Description:', twitter.getDescription())
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### `getImage`
|
|
190
|
+
|
|
191
|
+
Получает URL изображения карточки.
|
|
192
|
+
|
|
193
|
+
**Возвращает:** `string` — URL изображения
|
|
194
|
+
|
|
195
|
+
```javascript
|
|
196
|
+
const twitter = new MetaTwitter()
|
|
197
|
+
|
|
198
|
+
const imageUrl = twitter.getImage()
|
|
199
|
+
// 'https://example.com/images/twitter-card.jpg'
|
|
200
|
+
|
|
201
|
+
// Предзагрузка изображения
|
|
202
|
+
const img = new Image()
|
|
203
|
+
img.src = twitter.getImage()
|
|
204
|
+
|
|
205
|
+
// Проверка наличия
|
|
206
|
+
if (!twitter.getImage()) {
|
|
207
|
+
console.warn('Twitter изображение не установлено')
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Использование для превью
|
|
211
|
+
const previewImage = twitter.getImage()
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## Методы установки данных
|
|
215
|
+
|
|
216
|
+
### `setCard`
|
|
217
|
+
|
|
218
|
+
Устанавливает тип Twitter Card.
|
|
219
|
+
|
|
220
|
+
**Параметры:**
|
|
221
|
+
- `card: MetaTwitterCard` — тип карточки
|
|
222
|
+
|
|
223
|
+
**Возвращает:** `this` — для цепочечных вызовов
|
|
224
|
+
|
|
225
|
+
**Доступные типы:**
|
|
226
|
+
- `summary` — карточка-сводка с небольшим изображением
|
|
227
|
+
- `summary_large_image` — карточка с большим изображением (самый популярный)
|
|
228
|
+
- `app` — карточка приложения (для iOS/Android)
|
|
229
|
+
- `player` — карточка с видео/аудио плеером
|
|
230
|
+
|
|
231
|
+
```javascript
|
|
232
|
+
import { MetaTwitter, MetaTwitterCard } from '@dxtmisha/functional'
|
|
233
|
+
|
|
234
|
+
const twitter = new MetaTwitter()
|
|
235
|
+
|
|
236
|
+
// Установка стандартной карточки
|
|
237
|
+
twitter.setCard(MetaTwitterCard.summary)
|
|
238
|
+
|
|
239
|
+
// Карточка с большим изображением (рекомендуется)
|
|
240
|
+
twitter.setCard(MetaTwitterCard.summaryLargeImage)
|
|
241
|
+
|
|
242
|
+
// Для приложений
|
|
243
|
+
twitter.setCard(MetaTwitterCard.app)
|
|
244
|
+
|
|
245
|
+
// Для видео контента
|
|
246
|
+
twitter.setCard(MetaTwitterCard.player)
|
|
247
|
+
|
|
248
|
+
// Условная установка
|
|
249
|
+
const cardType = hasLargeImage
|
|
250
|
+
? MetaTwitterCard.summaryLargeImage
|
|
251
|
+
: MetaTwitterCard.summary
|
|
252
|
+
|
|
253
|
+
twitter.setCard(cardType)
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### `setSite`
|
|
257
|
+
|
|
258
|
+
Устанавливает Twitter аккаунт сайта или бренда.
|
|
259
|
+
|
|
260
|
+
**Параметры:**
|
|
261
|
+
- `site: string` — @username сайта
|
|
262
|
+
|
|
263
|
+
**Возвращает:** `this` — для цепочечных вызовов
|
|
264
|
+
|
|
265
|
+
```javascript
|
|
266
|
+
const twitter = new MetaTwitter()
|
|
267
|
+
|
|
268
|
+
// Установка аккаунта сайта
|
|
269
|
+
twitter.setSite('@mywebsite')
|
|
270
|
+
|
|
271
|
+
// Можно без @
|
|
272
|
+
twitter.setSite('mywebsite')
|
|
273
|
+
|
|
274
|
+
// Из конфигурации
|
|
275
|
+
twitter.setSite(config.twitterHandle)
|
|
276
|
+
|
|
277
|
+
// Цепочка методов
|
|
278
|
+
twitter
|
|
279
|
+
.setSite('@mywebsite')
|
|
280
|
+
.setCreator('@author')
|
|
281
|
+
|
|
282
|
+
// Рекомендации:
|
|
283
|
+
// - Используйте официальный аккаунт бренда
|
|
284
|
+
// - Формат: @username (с @ или без)
|
|
285
|
+
// - Twitter будет показывать "via @username" в карточке
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### `setCreator`
|
|
289
|
+
|
|
290
|
+
Устанавливает Twitter аккаунт автора контента.
|
|
291
|
+
|
|
292
|
+
**Параметры:**
|
|
293
|
+
- `creator: string` — @username автора
|
|
294
|
+
|
|
295
|
+
**Возвращает:** `this` — для цепочечных вызовов
|
|
296
|
+
|
|
297
|
+
```javascript
|
|
298
|
+
const twitter = new MetaTwitter()
|
|
299
|
+
|
|
300
|
+
// Установка автора
|
|
301
|
+
twitter.setCreator('@john_doe')
|
|
302
|
+
|
|
303
|
+
// Из данных пользователя
|
|
304
|
+
twitter.setCreator(article.author.twitterHandle)
|
|
305
|
+
|
|
306
|
+
// Динамическое обновление
|
|
307
|
+
const updateAuthor = (author) => {
|
|
308
|
+
if (author.twitter) {
|
|
309
|
+
twitter.setCreator(author.twitter)
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Цепочка
|
|
314
|
+
twitter
|
|
315
|
+
.setCreator('@author_name')
|
|
316
|
+
.setTitle('Заголовок статьи')
|
|
317
|
+
|
|
318
|
+
// Рекомендации:
|
|
319
|
+
// - Используйте для атрибуции автора
|
|
320
|
+
// - Twitter покажет "by @username"
|
|
321
|
+
// - Отличается от site (site - для сайта, creator - для автора)
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
### `setUrl`
|
|
325
|
+
|
|
326
|
+
Устанавливает URL страницы для Twitter Card.
|
|
327
|
+
|
|
328
|
+
**Параметры:**
|
|
329
|
+
- `url: string` — URL страницы
|
|
330
|
+
|
|
331
|
+
**Возвращает:** `this` — для цепочечных вызовов
|
|
332
|
+
|
|
333
|
+
```javascript
|
|
334
|
+
const twitter = new MetaTwitter()
|
|
335
|
+
|
|
336
|
+
// Установка URL
|
|
337
|
+
twitter.setUrl('https://example.com/article/my-post')
|
|
338
|
+
|
|
339
|
+
// Использование текущего URL
|
|
340
|
+
twitter.setUrl(window.location.href)
|
|
341
|
+
|
|
342
|
+
// Очистка query параметров
|
|
343
|
+
const cleanUrl = window.location.origin + window.location.pathname
|
|
344
|
+
twitter.setUrl(cleanUrl)
|
|
345
|
+
|
|
346
|
+
// Для SPA
|
|
347
|
+
router.afterEach((to) => {
|
|
348
|
+
twitter.setUrl(`https://example.com${to.fullPath}`)
|
|
349
|
+
})
|
|
350
|
+
|
|
351
|
+
// Абсолютный URL из относительного
|
|
352
|
+
const absoluteUrl = new URL('/article', window.location.origin).href
|
|
353
|
+
twitter.setUrl(absoluteUrl)
|
|
354
|
+
|
|
355
|
+
// Рекомендации:
|
|
356
|
+
// - Всегда используйте абсолютные URL
|
|
357
|
+
// - URL должен быть доступен для Twitter бота
|
|
358
|
+
// - Избегайте редиректов
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
### `setTitle`
|
|
362
|
+
|
|
363
|
+
Устанавливает заголовок карточки.
|
|
364
|
+
|
|
365
|
+
**Параметры:**
|
|
366
|
+
- `title: string` — заголовок карточки
|
|
367
|
+
|
|
368
|
+
**Возвращает:** `this` — для цепочечных вызовов
|
|
369
|
+
|
|
370
|
+
```javascript
|
|
371
|
+
const twitter = new MetaTwitter()
|
|
372
|
+
|
|
373
|
+
// Установка заголовка
|
|
374
|
+
twitter.setTitle('Потрясающая статья о веб-разработке')
|
|
375
|
+
|
|
376
|
+
// Цепочка методов
|
|
377
|
+
twitter
|
|
378
|
+
.setTitle('Новый заголовок')
|
|
379
|
+
.setDescription('Новое описание')
|
|
380
|
+
|
|
381
|
+
// Динамическое обновление
|
|
382
|
+
const updateTitle = (newTitle) => {
|
|
383
|
+
twitter.setTitle(newTitle)
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// Автоматическая генерация
|
|
387
|
+
twitter.setTitle(`${article.title} - ${siteName}`)
|
|
388
|
+
|
|
389
|
+
// Обрезка длинного заголовка
|
|
390
|
+
const maxLength = 70
|
|
391
|
+
const shortTitle = article.title.length > maxLength
|
|
392
|
+
? article.title.substring(0, maxLength - 3) + '...'
|
|
393
|
+
: article.title
|
|
394
|
+
|
|
395
|
+
twitter.setTitle(shortTitle)
|
|
396
|
+
|
|
397
|
+
// Рекомендации:
|
|
398
|
+
// - Максимум 70 символов
|
|
399
|
+
// - Будет отображаться жирным шрифтом
|
|
400
|
+
// - Избегайте дублирования с описанием
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
### `setDescription`
|
|
404
|
+
|
|
405
|
+
Устанавливает описание карточки.
|
|
406
|
+
|
|
407
|
+
**Параметры:**
|
|
408
|
+
- `description: string` — описание карточки
|
|
409
|
+
|
|
410
|
+
**Возвращает:** `this` — для цепочечных вызовов
|
|
411
|
+
|
|
412
|
+
```javascript
|
|
413
|
+
const twitter = new MetaTwitter()
|
|
414
|
+
|
|
415
|
+
// Установка описания
|
|
416
|
+
twitter.setDescription('Полное руководство по созданию веб-приложений с использованием современных технологий')
|
|
417
|
+
|
|
418
|
+
// Обрезка длинного текста
|
|
419
|
+
const shortDesc = article.content.substring(0, 200)
|
|
420
|
+
twitter.setDescription(shortDesc)
|
|
421
|
+
|
|
422
|
+
// Удаление HTML тегов
|
|
423
|
+
const plainText = article.html.replace(/<[^>]*>/g, '')
|
|
424
|
+
twitter.setDescription(plainText.substring(0, 200))
|
|
425
|
+
|
|
426
|
+
// Из excerpt
|
|
427
|
+
twitter.setDescription(article.excerpt || article.content.substring(0, 200))
|
|
428
|
+
|
|
429
|
+
// Цепочка
|
|
430
|
+
twitter
|
|
431
|
+
.setDescription('Описание статьи')
|
|
432
|
+
.setImage('https://example.com/image.jpg')
|
|
433
|
+
|
|
434
|
+
// Рекомендации:
|
|
435
|
+
// - Максимум 200 символов
|
|
436
|
+
// - Twitter может обрезать текст
|
|
437
|
+
// - Добавляйте call-to-action
|
|
438
|
+
// - Избегайте специальных символов
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
### `setImage`
|
|
442
|
+
|
|
443
|
+
Устанавливает URL изображения карточки.
|
|
444
|
+
|
|
445
|
+
**Параметры:**
|
|
446
|
+
- `image: string` — URL изображения
|
|
447
|
+
|
|
448
|
+
**Возвращает:** `this` — для цепочечных вызовов
|
|
449
|
+
|
|
450
|
+
```javascript
|
|
451
|
+
const twitter = new MetaTwitter()
|
|
452
|
+
|
|
453
|
+
// Установка изображения
|
|
454
|
+
twitter.setImage('https://example.com/images/twitter-card.jpg')
|
|
455
|
+
|
|
456
|
+
// Абсолютный URL
|
|
457
|
+
const imageUrl = new URL('/images/twitter-image.jpg', window.location.origin).href
|
|
458
|
+
twitter.setImage(imageUrl)
|
|
459
|
+
|
|
460
|
+
// Из данных контента
|
|
461
|
+
twitter.setImage(article.featuredImage)
|
|
462
|
+
|
|
463
|
+
// Fallback изображение
|
|
464
|
+
twitter.setImage(article.image || '/images/default-twitter.jpg')
|
|
465
|
+
|
|
466
|
+
// Для summary карточки (квадратное изображение)
|
|
467
|
+
twitter
|
|
468
|
+
.setCard(MetaTwitterCard.summary)
|
|
469
|
+
.setImage('https://example.com/square-image.jpg') // 1:1 ratio
|
|
470
|
+
|
|
471
|
+
// Для summary_large_image (широкое изображение)
|
|
472
|
+
twitter
|
|
473
|
+
.setCard(MetaTwitterCard.summaryLargeImage)
|
|
474
|
+
.setImage('https://example.com/wide-image.jpg') // 2:1 ratio
|
|
475
|
+
|
|
476
|
+
// Рекомендуемые размеры:
|
|
477
|
+
// summary: 120x120 - 1:1 (минимум)
|
|
478
|
+
// 280x150 - оптимально
|
|
479
|
+
// summary_large_image: 300x157 - минимум
|
|
480
|
+
// 1200x628 - оптимально (как OG)
|
|
481
|
+
// Ratio 2:1 или 1.91:1
|
|
482
|
+
// Форматы: JPG, PNG, WebP, GIF
|
|
483
|
+
// Максимальный размер: 5 MB
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
## Практические примеры
|
|
487
|
+
|
|
488
|
+
### Полная настройка страницы статьи
|
|
489
|
+
|
|
490
|
+
```javascript
|
|
491
|
+
import { MetaTwitter, MetaTwitterCard } from '@dxtmisha/functional'
|
|
492
|
+
|
|
493
|
+
const twitter = new MetaTwitter()
|
|
494
|
+
|
|
495
|
+
// Установка всех основных тегов
|
|
496
|
+
twitter
|
|
497
|
+
.setCard(MetaTwitterCard.summaryLargeImage)
|
|
498
|
+
.setTitle('Полное руководство по Twitter Cards')
|
|
499
|
+
.setDescription('Узнайте, как правильно настроить Twitter Card теги для вашего сайта и улучшить отображение в Twitter')
|
|
500
|
+
.setUrl('https://example.com/articles/twitter-cards-guide')
|
|
501
|
+
.setImage('https://example.com/images/twitter-guide.jpg')
|
|
502
|
+
.setSite('@mywebsite')
|
|
503
|
+
.setCreator('@author_name')
|
|
504
|
+
|
|
505
|
+
// Теперь при публикации в Twitter:
|
|
506
|
+
// - Отобразится красивая карточка с большим изображением
|
|
507
|
+
// - Будет показан заголовок и описание
|
|
508
|
+
// - Появится атрибуция "via @mywebsite by @author_name"
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
### Динамическое обновление для SPA
|
|
512
|
+
|
|
513
|
+
```javascript
|
|
514
|
+
// Vue Router
|
|
515
|
+
router.afterEach((to) => {
|
|
516
|
+
const twitter = new MetaTwitter()
|
|
517
|
+
|
|
518
|
+
twitter
|
|
519
|
+
.setCard(MetaTwitterCard.summaryLargeImage)
|
|
520
|
+
.setTitle(to.meta.twitterTitle || to.meta.title)
|
|
521
|
+
.setDescription(to.meta.twitterDescription || to.meta.description)
|
|
522
|
+
.setUrl(`https://example.com${to.path}`)
|
|
523
|
+
.setImage(to.meta.twitterImage || '/images/default-twitter.jpg')
|
|
524
|
+
.setSite('@mywebsite')
|
|
525
|
+
})
|
|
526
|
+
|
|
527
|
+
// React Router
|
|
528
|
+
useEffect(() => {
|
|
529
|
+
const twitter = new MetaTwitter()
|
|
530
|
+
|
|
531
|
+
twitter
|
|
532
|
+
.setCard(MetaTwitterCard.summaryLargeImage)
|
|
533
|
+
.setTitle(page.title)
|
|
534
|
+
.setDescription(page.description)
|
|
535
|
+
.setUrl(window.location.href)
|
|
536
|
+
.setImage(page.image)
|
|
537
|
+
.setSite(config.twitterSite)
|
|
538
|
+
.setCreator(page.author?.twitter)
|
|
539
|
+
|
|
540
|
+
return () => {
|
|
541
|
+
// Cleanup если необходимо
|
|
542
|
+
}
|
|
543
|
+
}, [page])
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
### Настройка для разных типов контента
|
|
547
|
+
|
|
548
|
+
```javascript
|
|
549
|
+
import { MetaTwitter, MetaTwitterCard } from '@dxtmisha/functional'
|
|
550
|
+
|
|
551
|
+
const twitter = new MetaTwitter()
|
|
552
|
+
|
|
553
|
+
// Для блог-поста
|
|
554
|
+
const setupBlogPost = (post) => {
|
|
555
|
+
twitter
|
|
556
|
+
.setCard(MetaTwitterCard.summaryLargeImage)
|
|
557
|
+
.setTitle(post.title)
|
|
558
|
+
.setDescription(post.excerpt)
|
|
559
|
+
.setUrl(`https://example.com/blog/${post.slug}`)
|
|
560
|
+
.setImage(post.coverImage)
|
|
561
|
+
.setSite('@myblog')
|
|
562
|
+
.setCreator(post.author.twitter)
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
// Для новости (с небольшим изображением)
|
|
566
|
+
const setupNews = (news) => {
|
|
567
|
+
twitter
|
|
568
|
+
.setCard(MetaTwitterCard.summary)
|
|
569
|
+
.setTitle(news.headline)
|
|
570
|
+
.setDescription(news.summary)
|
|
571
|
+
.setUrl(`https://example.com/news/${news.id}`)
|
|
572
|
+
.setImage(news.thumbnail)
|
|
573
|
+
.setSite('@mynews')
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
// Для видео
|
|
577
|
+
const setupVideo = (video) => {
|
|
578
|
+
twitter
|
|
579
|
+
.setCard(MetaTwitterCard.player)
|
|
580
|
+
.setTitle(video.title)
|
|
581
|
+
.setDescription(video.description)
|
|
582
|
+
.setUrl(`https://example.com/videos/${video.id}`)
|
|
583
|
+
.setImage(video.thumbnail)
|
|
584
|
+
.setSite('@myvideos')
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
// Для главной страницы
|
|
588
|
+
const setupHomePage = () => {
|
|
589
|
+
twitter
|
|
590
|
+
.setCard(MetaTwitterCard.summaryLargeImage)
|
|
591
|
+
.setTitle('Добро пожаловать на наш сайт')
|
|
592
|
+
.setDescription('Лучший контент о веб-разработке и технологиях')
|
|
593
|
+
.setUrl('https://example.com')
|
|
594
|
+
.setImage('https://example.com/images/home-twitter.jpg')
|
|
595
|
+
.setSite('@mywebsite')
|
|
596
|
+
}
|
|
597
|
+
```
|
|
598
|
+
|
|
599
|
+
### Генерация HTML для SSR
|
|
600
|
+
|
|
601
|
+
```javascript
|
|
602
|
+
import { MetaTwitter, MetaTwitterCard } from '@dxtmisha/functional'
|
|
603
|
+
|
|
604
|
+
// Express.js сервер
|
|
605
|
+
app.get('/article/:id', async (req, res) => {
|
|
606
|
+
const article = await getArticle(req.params.id)
|
|
607
|
+
|
|
608
|
+
const twitter = new MetaTwitter()
|
|
609
|
+
twitter
|
|
610
|
+
.setCard(MetaTwitterCard.summaryLargeImage)
|
|
611
|
+
.setTitle(article.title)
|
|
612
|
+
.setDescription(article.excerpt)
|
|
613
|
+
.setUrl(`https://example.com/article/${article.id}`)
|
|
614
|
+
.setImage(article.coverImage)
|
|
615
|
+
.setSite('@mywebsite')
|
|
616
|
+
.setCreator(article.author.twitter)
|
|
617
|
+
|
|
618
|
+
const html = `
|
|
619
|
+
<!DOCTYPE html>
|
|
620
|
+
<html>
|
|
621
|
+
<head>
|
|
622
|
+
<title>${article.title}</title>
|
|
623
|
+
${twitter.html()}
|
|
624
|
+
</head>
|
|
625
|
+
<body>
|
|
626
|
+
${article.content}
|
|
627
|
+
</body>
|
|
628
|
+
</html>
|
|
629
|
+
`
|
|
630
|
+
|
|
631
|
+
res.send(html)
|
|
632
|
+
})
|
|
633
|
+
|
|
634
|
+
// Результат в HTML:
|
|
635
|
+
// <meta property="twitter:card" content="summary_large_image">
|
|
636
|
+
// <meta property="twitter:title" content="Заголовок статьи">
|
|
637
|
+
// <meta property="twitter:description" content="Описание...">
|
|
638
|
+
// <meta property="twitter:url" content="https://example.com/article/123">
|
|
639
|
+
// <meta property="twitter:image" content="https://example.com/images/cover.jpg">
|
|
640
|
+
// <meta property="twitter:site" content="@mywebsite">
|
|
641
|
+
// <meta property="twitter:creator" content="@author">
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
### Комбинирование с Open Graph
|
|
645
|
+
|
|
646
|
+
```javascript
|
|
647
|
+
import { MetaTwitter, MetaOg, MetaTwitterCard, MetaOpenGraphType } from '@dxtmisha/functional'
|
|
648
|
+
|
|
649
|
+
const twitter = new MetaTwitter()
|
|
650
|
+
const og = new MetaOg()
|
|
651
|
+
|
|
652
|
+
// Общая функция обновления
|
|
653
|
+
const updateSocialMeta = (data) => {
|
|
654
|
+
// Twitter Cards
|
|
655
|
+
twitter
|
|
656
|
+
.setCard(MetaTwitterCard.summaryLargeImage)
|
|
657
|
+
.setTitle(data.title)
|
|
658
|
+
.setDescription(data.description)
|
|
659
|
+
.setUrl(data.url)
|
|
660
|
+
.setImage(data.image)
|
|
661
|
+
.setSite(data.twitterSite)
|
|
662
|
+
.setCreator(data.twitterCreator)
|
|
663
|
+
|
|
664
|
+
// Open Graph
|
|
665
|
+
og
|
|
666
|
+
.setType(MetaOpenGraphType.article)
|
|
667
|
+
.setTitle(data.title)
|
|
668
|
+
.setDescription(data.description)
|
|
669
|
+
.setUrl(data.url)
|
|
670
|
+
.setImage(data.image)
|
|
671
|
+
.setSiteName(data.siteName)
|
|
672
|
+
.setLocale('ru_RU')
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
// Использование
|
|
676
|
+
updateSocialMeta({
|
|
677
|
+
title: 'Заголовок статьи',
|
|
678
|
+
description: 'Описание статьи',
|
|
679
|
+
url: 'https://example.com/article/123',
|
|
680
|
+
image: 'https://example.com/images/cover.jpg',
|
|
681
|
+
twitterSite: '@mywebsite',
|
|
682
|
+
twitterCreator: '@author',
|
|
683
|
+
siteName: 'Мой Сайт'
|
|
684
|
+
})
|
|
685
|
+
|
|
686
|
+
// Для SSR - объединенный HTML
|
|
687
|
+
const allSocialMetaHTML = twitter.html() + og.html()
|
|
688
|
+
```
|
|
689
|
+
|
|
690
|
+
### Интеграция с CMS
|
|
691
|
+
|
|
692
|
+
```javascript
|
|
693
|
+
import { MetaTwitter, MetaTwitterCard } from '@dxtmisha/functional'
|
|
694
|
+
|
|
695
|
+
// Функция обновления Twitter Card из CMS данных
|
|
696
|
+
const updateTwitterFromCMS = (pageData) => {
|
|
697
|
+
const twitter = new MetaTwitter()
|
|
698
|
+
|
|
699
|
+
// Определение типа карточки
|
|
700
|
+
const cardMap = {
|
|
701
|
+
'post': MetaTwitterCard.summaryLargeImage,
|
|
702
|
+
'news': MetaTwitterCard.summary,
|
|
703
|
+
'video': MetaTwitterCard.player
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
twitter
|
|
707
|
+
.setCard(cardMap[pageData.contentType] || MetaTwitterCard.summaryLargeImage)
|
|
708
|
+
.setTitle(pageData.seo?.twitterTitle || pageData.title)
|
|
709
|
+
.setDescription(pageData.seo?.twitterDescription || pageData.excerpt)
|
|
710
|
+
.setUrl(pageData.canonicalUrl)
|
|
711
|
+
.setImage(pageData.seo?.twitterImage || pageData.featuredImage)
|
|
712
|
+
.setSite(pageData.site.twitterHandle)
|
|
713
|
+
|
|
714
|
+
// Добавление автора если есть
|
|
715
|
+
if (pageData.author?.twitter) {
|
|
716
|
+
twitter.setCreator(pageData.author.twitter)
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
return twitter
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
// Использование
|
|
723
|
+
const page = await cms.getPage(pageId)
|
|
724
|
+
updateTwitterFromCMS(page)
|
|
725
|
+
```
|
|
726
|
+
|
|
727
|
+
### Валидация и отладка
|
|
728
|
+
|
|
729
|
+
```javascript
|
|
730
|
+
import { MetaTwitter } from '@dxtmisha/functional'
|
|
731
|
+
|
|
732
|
+
const twitter = new MetaTwitter()
|
|
733
|
+
|
|
734
|
+
// Проверка обязательных полей
|
|
735
|
+
const validateTwitterCard = () => {
|
|
736
|
+
const errors = []
|
|
737
|
+
|
|
738
|
+
if (!twitter.getCard()) errors.push('Отсутствует twitter:card')
|
|
739
|
+
if (!twitter.getTitle()) errors.push('Отсутствует twitter:title')
|
|
740
|
+
if (!twitter.getDescription()) errors.push('Отсутствует twitter:description')
|
|
741
|
+
if (!twitter.getImage()) errors.push('Отсутствует twitter:image')
|
|
742
|
+
|
|
743
|
+
// Проверка длины
|
|
744
|
+
if (twitter.getTitle().length > 70) {
|
|
745
|
+
errors.push('twitter:title слишком длинный (>70 символов)')
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
if (twitter.getDescription().length > 200) {
|
|
749
|
+
errors.push('twitter:description слишком длинное (>200 символов)')
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
if (errors.length > 0) {
|
|
753
|
+
console.error('Twitter Card ошибки:', errors)
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
return errors.length === 0
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
// Вывод всех Twitter Card тегов для отладки
|
|
760
|
+
const debugTwitterCard = () => {
|
|
761
|
+
console.group('Twitter Card Tags')
|
|
762
|
+
console.log('Card Type:', twitter.getCard())
|
|
763
|
+
console.log('Title:', twitter.getTitle())
|
|
764
|
+
console.log('Description:', twitter.getDescription())
|
|
765
|
+
console.log('Image:', twitter.getImage())
|
|
766
|
+
console.log('URL:', twitter.getUrl())
|
|
767
|
+
console.log('Site:', twitter.getSite())
|
|
768
|
+
console.log('Creator:', twitter.getCreator())
|
|
769
|
+
console.groupEnd()
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
// Экспорт для тестирования
|
|
773
|
+
const exportTwitterData = () => {
|
|
774
|
+
return twitter.getItems()
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
// Проверка перед публикацией
|
|
778
|
+
const checkBeforePublish = () => {
|
|
779
|
+
const isValid = validateTwitterCard()
|
|
780
|
+
|
|
781
|
+
if (isValid) {
|
|
782
|
+
console.log('✓ Twitter Card настроен правильно')
|
|
783
|
+
} else {
|
|
784
|
+
console.warn('⚠ Twitter Card требует внимания')
|
|
785
|
+
debugTwitterCard()
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
return isValid
|
|
789
|
+
}
|
|
790
|
+
```
|
|
791
|
+
|
|
792
|
+
## Рекомендации по использованию
|
|
793
|
+
|
|
794
|
+
### Обязательные теги
|
|
795
|
+
Для корректного отображения Twitter Card необходимо установить минимум:
|
|
796
|
+
- `twitter:card` — тип карточки
|
|
797
|
+
- `twitter:title` — заголовок
|
|
798
|
+
- `twitter:description` — описание
|
|
799
|
+
- `twitter:image` — изображение
|
|
800
|
+
|
|
801
|
+
### Размеры изображений
|
|
802
|
+
|
|
803
|
+
**summary карточка:**
|
|
804
|
+
- **Рекомендуемый размер:** 120×120 px (минимум)
|
|
805
|
+
- **Оптимальный размер:** 280×150 px
|
|
806
|
+
- **Соотношение сторон:** 1:1 (квадратное)
|
|
807
|
+
|
|
808
|
+
**summary_large_image карточка:**
|
|
809
|
+
- **Минимальный размер:** 300×157 px
|
|
810
|
+
- **Оптимальный размер:** 1200×628 px (как Open Graph)
|
|
811
|
+
- **Соотношение сторон:** 2:1 или 1.91:1
|
|
812
|
+
- **Максимальный размер файла:** 5 MB
|
|
813
|
+
- **Форматы:** JPG, PNG, WebP, GIF
|
|
814
|
+
|
|
815
|
+
### Длина текста
|
|
816
|
+
- **twitter:title:** до 70 символов
|
|
817
|
+
- **twitter:description:** до 200 символов
|
|
818
|
+
- **twitter:site:** @username формат
|
|
819
|
+
- **twitter:creator:** @username формат
|
|
820
|
+
|
|
821
|
+
### Атрибуты @username
|
|
822
|
+
- **twitter:site** — официальный Twitter аккаунт сайта/бренда
|
|
823
|
+
- Показывается как "via @username"
|
|
824
|
+
- Используется для атрибуции сайта
|
|
825
|
+
- **twitter:creator** — Twitter аккаунт автора контента
|
|
826
|
+
- Показывается как "by @username"
|
|
827
|
+
- Используется для атрибуции автора
|
|
828
|
+
|
|
829
|
+
### Тестирование
|
|
830
|
+
Проверяйте Twitter Card теги с помощью:
|
|
831
|
+
- [Twitter Card Validator](https://cards-dev.twitter.com/validator) (старая версия)
|
|
832
|
+
- Тестируйте, публикуя тестовую ссылку в Twitter
|
|
833
|
+
- Используйте режим предпросмотра при создании твита
|
|
834
|
+
|
|
835
|
+
### Различия с Open Graph
|
|
836
|
+
Twitter Cards может использовать Open Graph теги как fallback:
|
|
837
|
+
- Если `twitter:title` отсутствует, используется `og:title`
|
|
838
|
+
- Если `twitter:description` отсутствует, используется `og:description`
|
|
839
|
+
- Если `twitter:image` отсутствует, используется `og:image`
|
|
840
|
+
|
|
841
|
+
Однако рекомендуется устанавливать Twitter Card теги явно для лучшего контроля.
|
|
842
|
+
|
|
843
|
+
## Примечания
|
|
844
|
+
|
|
845
|
+
- Класс наследует все методы от `MetaManager`, включая `html()`, `getItems()`, `setByList()`
|
|
846
|
+
- Автоматически использует атрибут `property` (как и Open Graph)
|
|
847
|
+
- Все изменения немедленно отражаются в DOM дереве
|
|
848
|
+
- При создании экземпляра автоматически считываются существующие Twitter Card теги из страницы
|
|
849
|
+
- Для SSR используйте метод `html()` для генерации мета-тегов в серверном шаблоне
|
|
850
|
+
- Содержимое автоматически экранируется для предотвращения XSS
|
|
851
|
+
- Twitter может кэшировать карточки, используйте Card Validator для сброса кэша
|
|
852
|
+
- Поддерживаются все официальные типы Twitter Cards
|
|
853
|
+
|