@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/en/Classes/MetaTwitter'/>
|
|
4
|
+
|
|
5
|
+
# MetaTwitter Class
|
|
6
|
+
|
|
7
|
+
A class for working with Twitter Card meta-tags, ensuring beautiful content display when sharing links on Twitter (X). Inherits `MetaManager` functionality and provides type-safe methods for managing standard Twitter Card tags.
|
|
8
|
+
|
|
9
|
+
## Key Features
|
|
10
|
+
|
|
11
|
+
- **Type Safety** — strict typing for all Twitter Card tags and card types
|
|
12
|
+
- **Automatic Synchronization** — updates meta-tags in the document `<head>`
|
|
13
|
+
- **Support for All Card Types** — summary, summary_large_image, app, player
|
|
14
|
+
- **Convenient API** — specialized methods for each tag
|
|
15
|
+
- **SSR Compatibility** — HTML generation for server-side rendering
|
|
16
|
+
- **Type Validation** — support for all official Twitter Card types
|
|
17
|
+
- **Chainable Methods** — ability to set values sequentially
|
|
18
|
+
|
|
19
|
+
## Constructor
|
|
20
|
+
|
|
21
|
+
### `constructor`
|
|
22
|
+
|
|
23
|
+
Creates a MetaTwitter instance with a preset list of all supported Twitter Card tags. Automatically reads existing tags from the DOM.
|
|
24
|
+
|
|
25
|
+
```javascript
|
|
26
|
+
import { MetaTwitter } from '@dxtmisha/functional'
|
|
27
|
+
|
|
28
|
+
// Create an instance
|
|
29
|
+
const twitter = new MetaTwitter()
|
|
30
|
+
|
|
31
|
+
// The class automatically manages all standard Twitter Card tags:
|
|
32
|
+
// twitter:card, twitter:site, twitter:creator, twitter:title,
|
|
33
|
+
// twitter:description, twitter:image and others
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Data Retrieval Methods
|
|
37
|
+
|
|
38
|
+
### `getCard`
|
|
39
|
+
|
|
40
|
+
Gets the Twitter Card type.
|
|
41
|
+
|
|
42
|
+
**Returns:** `MetaTwitterCard` — card type
|
|
43
|
+
|
|
44
|
+
```javascript
|
|
45
|
+
const twitter = new MetaTwitter()
|
|
46
|
+
|
|
47
|
+
const cardType = twitter.getCard()
|
|
48
|
+
// 'summary_large_image'
|
|
49
|
+
|
|
50
|
+
// Check card type
|
|
51
|
+
if (twitter.getCard() === 'summary_large_image') {
|
|
52
|
+
console.log('Large image card')
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Conditional logic
|
|
56
|
+
switch (twitter.getCard()) {
|
|
57
|
+
case 'summary':
|
|
58
|
+
console.log('Standard card')
|
|
59
|
+
break
|
|
60
|
+
case 'summary_large_image':
|
|
61
|
+
console.log('Large image card')
|
|
62
|
+
break
|
|
63
|
+
case 'player':
|
|
64
|
+
console.log('Video player')
|
|
65
|
+
break
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### `getSite`
|
|
70
|
+
|
|
71
|
+
Gets the Twitter account of the website or brand.
|
|
72
|
+
|
|
73
|
+
**Returns:** `string` — site's @username
|
|
74
|
+
|
|
75
|
+
```javascript
|
|
76
|
+
const twitter = new MetaTwitter()
|
|
77
|
+
|
|
78
|
+
const site = twitter.getSite()
|
|
79
|
+
// '@mywebsite'
|
|
80
|
+
|
|
81
|
+
// Use for attribution
|
|
82
|
+
console.log('Site belongs to:', twitter.getSite())
|
|
83
|
+
|
|
84
|
+
// Check if set
|
|
85
|
+
if (twitter.getSite()) {
|
|
86
|
+
console.log('Twitter site account is set')
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Extract username without @
|
|
90
|
+
const username = twitter.getSite().replace('@', '')
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### `getCreator`
|
|
94
|
+
|
|
95
|
+
Gets the Twitter account of the content creator.
|
|
96
|
+
|
|
97
|
+
**Returns:** `string` — creator's @username
|
|
98
|
+
|
|
99
|
+
```javascript
|
|
100
|
+
const twitter = new MetaTwitter()
|
|
101
|
+
|
|
102
|
+
const creator = twitter.getCreator()
|
|
103
|
+
// '@john_doe'
|
|
104
|
+
|
|
105
|
+
// Display author
|
|
106
|
+
console.log('Author:', twitter.getCreator())
|
|
107
|
+
|
|
108
|
+
// Use in UI
|
|
109
|
+
const authorLink = `https://twitter.com/${twitter.getCreator().replace('@', '')}`
|
|
110
|
+
|
|
111
|
+
// Check authorship
|
|
112
|
+
if (twitter.getCreator()) {
|
|
113
|
+
console.log('Content has an author')
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### `getUrl`
|
|
118
|
+
|
|
119
|
+
Gets the page URL for Twitter Card.
|
|
120
|
+
|
|
121
|
+
**Returns:** `string` — page URL
|
|
122
|
+
|
|
123
|
+
```javascript
|
|
124
|
+
const twitter = new MetaTwitter()
|
|
125
|
+
|
|
126
|
+
const url = twitter.getUrl()
|
|
127
|
+
// 'https://example.com/article/my-post'
|
|
128
|
+
|
|
129
|
+
// Get domain
|
|
130
|
+
const domain = new URL(twitter.getUrl()).hostname
|
|
131
|
+
|
|
132
|
+
// Check match with current URL
|
|
133
|
+
if (twitter.getUrl() !== window.location.href) {
|
|
134
|
+
console.warn('Twitter URL does not match current URL')
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Create share link
|
|
138
|
+
const shareUrl = `https://twitter.com/intent/tweet?url=${encodeURIComponent(twitter.getUrl())}`
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### `getTitle`
|
|
142
|
+
|
|
143
|
+
Gets the card title.
|
|
144
|
+
|
|
145
|
+
**Returns:** `string` — card title
|
|
146
|
+
|
|
147
|
+
```javascript
|
|
148
|
+
const twitter = new MetaTwitter()
|
|
149
|
+
|
|
150
|
+
const title = twitter.getTitle()
|
|
151
|
+
// 'Amazing Article About Web Development'
|
|
152
|
+
|
|
153
|
+
// Use for debugging
|
|
154
|
+
console.log('Twitter Title:', twitter.getTitle())
|
|
155
|
+
|
|
156
|
+
// Check length (recommended up to 70 characters)
|
|
157
|
+
if (twitter.getTitle().length > 70) {
|
|
158
|
+
console.warn('Title is too long for Twitter')
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Use in preview
|
|
162
|
+
const previewTitle = twitter.getTitle()
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### `getDescription`
|
|
166
|
+
|
|
167
|
+
Gets the card description.
|
|
168
|
+
|
|
169
|
+
**Returns:** `string` — card description
|
|
170
|
+
|
|
171
|
+
```javascript
|
|
172
|
+
const twitter = new MetaTwitter()
|
|
173
|
+
|
|
174
|
+
const description = twitter.getDescription()
|
|
175
|
+
// 'Complete guide to building...'
|
|
176
|
+
|
|
177
|
+
// Check length (recommended up to 200 characters)
|
|
178
|
+
if (twitter.getDescription().length > 200) {
|
|
179
|
+
console.warn('Description is too long')
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Truncate for preview
|
|
183
|
+
const shortDesc = twitter.getDescription().substring(0, 150) + '...'
|
|
184
|
+
|
|
185
|
+
// Use in metadata
|
|
186
|
+
console.log('Twitter Description:', twitter.getDescription())
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### `getImage`
|
|
190
|
+
|
|
191
|
+
Gets the card image URL.
|
|
192
|
+
|
|
193
|
+
**Returns:** `string` — image 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
|
+
// Preload image
|
|
202
|
+
const img = new Image()
|
|
203
|
+
img.src = twitter.getImage()
|
|
204
|
+
|
|
205
|
+
// Check if set
|
|
206
|
+
if (!twitter.getImage()) {
|
|
207
|
+
console.warn('Twitter image is not set')
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Use for preview
|
|
211
|
+
const previewImage = twitter.getImage()
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## Data Setting Methods
|
|
215
|
+
|
|
216
|
+
### `setCard`
|
|
217
|
+
|
|
218
|
+
Sets the Twitter Card type.
|
|
219
|
+
|
|
220
|
+
**Parameters:**
|
|
221
|
+
- `card: MetaTwitterCard` — card type
|
|
222
|
+
|
|
223
|
+
**Returns:** `this` — for chaining calls
|
|
224
|
+
|
|
225
|
+
**Available types:**
|
|
226
|
+
- `summary` — summary card with small image
|
|
227
|
+
- `summary_large_image` — card with large image (most popular)
|
|
228
|
+
- `app` — app card (for iOS/Android)
|
|
229
|
+
- `player` — card with video/audio player
|
|
230
|
+
|
|
231
|
+
```javascript
|
|
232
|
+
import { MetaTwitter, MetaTwitterCard } from '@dxtmisha/functional'
|
|
233
|
+
|
|
234
|
+
const twitter = new MetaTwitter()
|
|
235
|
+
|
|
236
|
+
// Set standard card
|
|
237
|
+
twitter.setCard(MetaTwitterCard.summary)
|
|
238
|
+
|
|
239
|
+
// Large image card (recommended)
|
|
240
|
+
twitter.setCard(MetaTwitterCard.summaryLargeImage)
|
|
241
|
+
|
|
242
|
+
// For apps
|
|
243
|
+
twitter.setCard(MetaTwitterCard.app)
|
|
244
|
+
|
|
245
|
+
// For video content
|
|
246
|
+
twitter.setCard(MetaTwitterCard.player)
|
|
247
|
+
|
|
248
|
+
// Conditional setting
|
|
249
|
+
const cardType = hasLargeImage
|
|
250
|
+
? MetaTwitterCard.summaryLargeImage
|
|
251
|
+
: MetaTwitterCard.summary
|
|
252
|
+
|
|
253
|
+
twitter.setCard(cardType)
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### `setSite`
|
|
257
|
+
|
|
258
|
+
Sets the Twitter account of the website or brand.
|
|
259
|
+
|
|
260
|
+
**Parameters:**
|
|
261
|
+
- `site: string` — site's @username
|
|
262
|
+
|
|
263
|
+
**Returns:** `this` — for chaining calls
|
|
264
|
+
|
|
265
|
+
```javascript
|
|
266
|
+
const twitter = new MetaTwitter()
|
|
267
|
+
|
|
268
|
+
// Set site account
|
|
269
|
+
twitter.setSite('@mywebsite')
|
|
270
|
+
|
|
271
|
+
// Can be without @
|
|
272
|
+
twitter.setSite('mywebsite')
|
|
273
|
+
|
|
274
|
+
// From configuration
|
|
275
|
+
twitter.setSite(config.twitterHandle)
|
|
276
|
+
|
|
277
|
+
// Chain methods
|
|
278
|
+
twitter
|
|
279
|
+
.setSite('@mywebsite')
|
|
280
|
+
.setCreator('@author')
|
|
281
|
+
|
|
282
|
+
// Recommendations:
|
|
283
|
+
// - Use official brand account
|
|
284
|
+
// - Format: @username (with or without @)
|
|
285
|
+
// - Twitter will show "via @username" in the card
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### `setCreator`
|
|
289
|
+
|
|
290
|
+
Sets the Twitter account of the content creator.
|
|
291
|
+
|
|
292
|
+
**Parameters:**
|
|
293
|
+
- `creator: string` — creator's @username
|
|
294
|
+
|
|
295
|
+
**Returns:** `this` — for chaining calls
|
|
296
|
+
|
|
297
|
+
```javascript
|
|
298
|
+
const twitter = new MetaTwitter()
|
|
299
|
+
|
|
300
|
+
// Set creator
|
|
301
|
+
twitter.setCreator('@john_doe')
|
|
302
|
+
|
|
303
|
+
// From user data
|
|
304
|
+
twitter.setCreator(article.author.twitterHandle)
|
|
305
|
+
|
|
306
|
+
// Dynamic update
|
|
307
|
+
const updateAuthor = (author) => {
|
|
308
|
+
if (author.twitter) {
|
|
309
|
+
twitter.setCreator(author.twitter)
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Chain
|
|
314
|
+
twitter
|
|
315
|
+
.setCreator('@author_name')
|
|
316
|
+
.setTitle('Article Title')
|
|
317
|
+
|
|
318
|
+
// Recommendations:
|
|
319
|
+
// - Use for author attribution
|
|
320
|
+
// - Twitter will show "by @username"
|
|
321
|
+
// - Different from site (site - for website, creator - for author)
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
### `setUrl`
|
|
325
|
+
|
|
326
|
+
Sets the page URL for Twitter Card.
|
|
327
|
+
|
|
328
|
+
**Parameters:**
|
|
329
|
+
- `url: string` — page URL
|
|
330
|
+
|
|
331
|
+
**Returns:** `this` — for chaining calls
|
|
332
|
+
|
|
333
|
+
```javascript
|
|
334
|
+
const twitter = new MetaTwitter()
|
|
335
|
+
|
|
336
|
+
// Set URL
|
|
337
|
+
twitter.setUrl('https://example.com/article/my-post')
|
|
338
|
+
|
|
339
|
+
// Use current URL
|
|
340
|
+
twitter.setUrl(window.location.href)
|
|
341
|
+
|
|
342
|
+
// Clean query parameters
|
|
343
|
+
const cleanUrl = window.location.origin + window.location.pathname
|
|
344
|
+
twitter.setUrl(cleanUrl)
|
|
345
|
+
|
|
346
|
+
// For SPA
|
|
347
|
+
router.afterEach((to) => {
|
|
348
|
+
twitter.setUrl(`https://example.com${to.fullPath}`)
|
|
349
|
+
})
|
|
350
|
+
|
|
351
|
+
// Absolute URL from relative
|
|
352
|
+
const absoluteUrl = new URL('/article', window.location.origin).href
|
|
353
|
+
twitter.setUrl(absoluteUrl)
|
|
354
|
+
|
|
355
|
+
// Recommendations:
|
|
356
|
+
// - Always use absolute URLs
|
|
357
|
+
// - URL must be accessible to Twitter bot
|
|
358
|
+
// - Avoid redirects
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
### `setTitle`
|
|
362
|
+
|
|
363
|
+
Sets the card title.
|
|
364
|
+
|
|
365
|
+
**Parameters:**
|
|
366
|
+
- `title: string` — card title
|
|
367
|
+
|
|
368
|
+
**Returns:** `this` — for chaining calls
|
|
369
|
+
|
|
370
|
+
```javascript
|
|
371
|
+
const twitter = new MetaTwitter()
|
|
372
|
+
|
|
373
|
+
// Set title
|
|
374
|
+
twitter.setTitle('Amazing Article About Web Development')
|
|
375
|
+
|
|
376
|
+
// Chain methods
|
|
377
|
+
twitter
|
|
378
|
+
.setTitle('New Title')
|
|
379
|
+
.setDescription('New Description')
|
|
380
|
+
|
|
381
|
+
// Dynamic update
|
|
382
|
+
const updateTitle = (newTitle) => {
|
|
383
|
+
twitter.setTitle(newTitle)
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// Automatic generation
|
|
387
|
+
twitter.setTitle(`${article.title} - ${siteName}`)
|
|
388
|
+
|
|
389
|
+
// Truncate long title
|
|
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
|
+
// Recommendations:
|
|
398
|
+
// - Maximum 70 characters
|
|
399
|
+
// - Will be displayed in bold
|
|
400
|
+
// - Avoid duplication with description
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
### `setDescription`
|
|
404
|
+
|
|
405
|
+
Sets the card description.
|
|
406
|
+
|
|
407
|
+
**Parameters:**
|
|
408
|
+
- `description: string` — card description
|
|
409
|
+
|
|
410
|
+
**Returns:** `this` — for chaining calls
|
|
411
|
+
|
|
412
|
+
```javascript
|
|
413
|
+
const twitter = new MetaTwitter()
|
|
414
|
+
|
|
415
|
+
// Set description
|
|
416
|
+
twitter.setDescription('Complete guide to building web applications using modern technologies')
|
|
417
|
+
|
|
418
|
+
// Truncate long text
|
|
419
|
+
const shortDesc = article.content.substring(0, 200)
|
|
420
|
+
twitter.setDescription(shortDesc)
|
|
421
|
+
|
|
422
|
+
// Remove HTML tags
|
|
423
|
+
const plainText = article.html.replace(/<[^>]*>/g, '')
|
|
424
|
+
twitter.setDescription(plainText.substring(0, 200))
|
|
425
|
+
|
|
426
|
+
// From excerpt
|
|
427
|
+
twitter.setDescription(article.excerpt || article.content.substring(0, 200))
|
|
428
|
+
|
|
429
|
+
// Chain
|
|
430
|
+
twitter
|
|
431
|
+
.setDescription('Article description')
|
|
432
|
+
.setImage('https://example.com/image.jpg')
|
|
433
|
+
|
|
434
|
+
// Recommendations:
|
|
435
|
+
// - Maximum 200 characters
|
|
436
|
+
// - Twitter may truncate text
|
|
437
|
+
// - Add call-to-action
|
|
438
|
+
// - Avoid special characters
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
### `setImage`
|
|
442
|
+
|
|
443
|
+
Sets the card image URL.
|
|
444
|
+
|
|
445
|
+
**Parameters:**
|
|
446
|
+
- `image: string` — image URL
|
|
447
|
+
|
|
448
|
+
**Returns:** `this` — for chaining calls
|
|
449
|
+
|
|
450
|
+
```javascript
|
|
451
|
+
const twitter = new MetaTwitter()
|
|
452
|
+
|
|
453
|
+
// Set image
|
|
454
|
+
twitter.setImage('https://example.com/images/twitter-card.jpg')
|
|
455
|
+
|
|
456
|
+
// Absolute URL
|
|
457
|
+
const imageUrl = new URL('/images/twitter-image.jpg', window.location.origin).href
|
|
458
|
+
twitter.setImage(imageUrl)
|
|
459
|
+
|
|
460
|
+
// From content data
|
|
461
|
+
twitter.setImage(article.featuredImage)
|
|
462
|
+
|
|
463
|
+
// Fallback image
|
|
464
|
+
twitter.setImage(article.image || '/images/default-twitter.jpg')
|
|
465
|
+
|
|
466
|
+
// For summary card (square image)
|
|
467
|
+
twitter
|
|
468
|
+
.setCard(MetaTwitterCard.summary)
|
|
469
|
+
.setImage('https://example.com/square-image.jpg') // 1:1 ratio
|
|
470
|
+
|
|
471
|
+
// For summary_large_image (wide image)
|
|
472
|
+
twitter
|
|
473
|
+
.setCard(MetaTwitterCard.summaryLargeImage)
|
|
474
|
+
.setImage('https://example.com/wide-image.jpg') // 2:1 ratio
|
|
475
|
+
|
|
476
|
+
// Recommended sizes:
|
|
477
|
+
// summary: 120x120 - 1:1 (minimum)
|
|
478
|
+
// 280x150 - optimal
|
|
479
|
+
// summary_large_image: 300x157 - minimum
|
|
480
|
+
// 1200x628 - optimal (like OG)
|
|
481
|
+
// Ratio 2:1 or 1.91:1
|
|
482
|
+
// Formats: JPG, PNG, WebP, GIF
|
|
483
|
+
// Maximum size: 5 MB
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
## Practical Examples
|
|
487
|
+
|
|
488
|
+
### Complete Article Page Setup
|
|
489
|
+
|
|
490
|
+
```javascript
|
|
491
|
+
import { MetaTwitter, MetaTwitterCard } from '@dxtmisha/functional'
|
|
492
|
+
|
|
493
|
+
const twitter = new MetaTwitter()
|
|
494
|
+
|
|
495
|
+
// Set all main tags
|
|
496
|
+
twitter
|
|
497
|
+
.setCard(MetaTwitterCard.summaryLargeImage)
|
|
498
|
+
.setTitle('Complete Guide to Twitter Cards')
|
|
499
|
+
.setDescription('Learn how to properly configure Twitter Card tags for your site and improve Twitter sharing')
|
|
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
|
+
// Now when shared on Twitter:
|
|
506
|
+
// - A beautiful card with large image will be displayed
|
|
507
|
+
// - Title and description will be shown
|
|
508
|
+
// - Attribution will appear as "via @mywebsite by @author_name"
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
### Dynamic Updates for 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 if needed
|
|
542
|
+
}
|
|
543
|
+
}, [page])
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
### Setup for Different Content Types
|
|
547
|
+
|
|
548
|
+
```javascript
|
|
549
|
+
import { MetaTwitter, MetaTwitterCard } from '@dxtmisha/functional'
|
|
550
|
+
|
|
551
|
+
const twitter = new MetaTwitter()
|
|
552
|
+
|
|
553
|
+
// For blog post
|
|
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
|
+
// For news (with small image)
|
|
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
|
+
// For video
|
|
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
|
+
// For homepage
|
|
588
|
+
const setupHomePage = () => {
|
|
589
|
+
twitter
|
|
590
|
+
.setCard(MetaTwitterCard.summaryLargeImage)
|
|
591
|
+
.setTitle('Welcome to Our Website')
|
|
592
|
+
.setDescription('The best content about web development and technology')
|
|
593
|
+
.setUrl('https://example.com')
|
|
594
|
+
.setImage('https://example.com/images/home-twitter.jpg')
|
|
595
|
+
.setSite('@mywebsite')
|
|
596
|
+
}
|
|
597
|
+
```
|
|
598
|
+
|
|
599
|
+
### HTML Generation for SSR
|
|
600
|
+
|
|
601
|
+
```javascript
|
|
602
|
+
import { MetaTwitter, MetaTwitterCard } from '@dxtmisha/functional'
|
|
603
|
+
|
|
604
|
+
// Express.js server
|
|
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
|
+
// Result in HTML:
|
|
635
|
+
// <meta property="twitter:card" content="summary_large_image">
|
|
636
|
+
// <meta property="twitter:title" content="Article Title">
|
|
637
|
+
// <meta property="twitter:description" content="Description...">
|
|
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
|
+
### Combining with 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
|
+
// Common update function
|
|
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('en_US')
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
// Usage
|
|
676
|
+
updateSocialMeta({
|
|
677
|
+
title: 'Article Title',
|
|
678
|
+
description: 'Article description',
|
|
679
|
+
url: 'https://example.com/article/123',
|
|
680
|
+
image: 'https://example.com/images/cover.jpg',
|
|
681
|
+
twitterSite: '@mywebsite',
|
|
682
|
+
twitterCreator: '@author',
|
|
683
|
+
siteName: 'My Website'
|
|
684
|
+
})
|
|
685
|
+
|
|
686
|
+
// For SSR - combined HTML
|
|
687
|
+
const allSocialMetaHTML = twitter.html() + og.html()
|
|
688
|
+
```
|
|
689
|
+
|
|
690
|
+
### CMS Integration
|
|
691
|
+
|
|
692
|
+
```javascript
|
|
693
|
+
import { MetaTwitter, MetaTwitterCard } from '@dxtmisha/functional'
|
|
694
|
+
|
|
695
|
+
// Function to update Twitter Card from CMS data
|
|
696
|
+
const updateTwitterFromCMS = (pageData) => {
|
|
697
|
+
const twitter = new MetaTwitter()
|
|
698
|
+
|
|
699
|
+
// Determine card type
|
|
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
|
+
// Add author if available
|
|
715
|
+
if (pageData.author?.twitter) {
|
|
716
|
+
twitter.setCreator(pageData.author.twitter)
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
return twitter
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
// Usage
|
|
723
|
+
const page = await cms.getPage(pageId)
|
|
724
|
+
updateTwitterFromCMS(page)
|
|
725
|
+
```
|
|
726
|
+
|
|
727
|
+
### Validation and Debugging
|
|
728
|
+
|
|
729
|
+
```javascript
|
|
730
|
+
import { MetaTwitter } from '@dxtmisha/functional'
|
|
731
|
+
|
|
732
|
+
const twitter = new MetaTwitter()
|
|
733
|
+
|
|
734
|
+
// Check required fields
|
|
735
|
+
const validateTwitterCard = () => {
|
|
736
|
+
const errors = []
|
|
737
|
+
|
|
738
|
+
if (!twitter.getCard()) errors.push('Missing twitter:card')
|
|
739
|
+
if (!twitter.getTitle()) errors.push('Missing twitter:title')
|
|
740
|
+
if (!twitter.getDescription()) errors.push('Missing twitter:description')
|
|
741
|
+
if (!twitter.getImage()) errors.push('Missing twitter:image')
|
|
742
|
+
|
|
743
|
+
// Check length
|
|
744
|
+
if (twitter.getTitle().length > 70) {
|
|
745
|
+
errors.push('twitter:title is too long (>70 characters)')
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
if (twitter.getDescription().length > 200) {
|
|
749
|
+
errors.push('twitter:description is too long (>200 characters)')
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
if (errors.length > 0) {
|
|
753
|
+
console.error('Twitter Card errors:', errors)
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
return errors.length === 0
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
// Output all Twitter Card tags for debugging
|
|
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
|
+
// Export for testing
|
|
773
|
+
const exportTwitterData = () => {
|
|
774
|
+
return twitter.getItems()
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
// Check before publishing
|
|
778
|
+
const checkBeforePublish = () => {
|
|
779
|
+
const isValid = validateTwitterCard()
|
|
780
|
+
|
|
781
|
+
if (isValid) {
|
|
782
|
+
console.log('✓ Twitter Card configured correctly')
|
|
783
|
+
} else {
|
|
784
|
+
console.warn('⚠ Twitter Card needs attention')
|
|
785
|
+
debugTwitterCard()
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
return isValid
|
|
789
|
+
}
|
|
790
|
+
```
|
|
791
|
+
|
|
792
|
+
## Usage Recommendations
|
|
793
|
+
|
|
794
|
+
### Required Tags
|
|
795
|
+
For correct Twitter Card display, set at minimum:
|
|
796
|
+
- `twitter:card` — card type
|
|
797
|
+
- `twitter:title` — title
|
|
798
|
+
- `twitter:description` — description
|
|
799
|
+
- `twitter:image` — image
|
|
800
|
+
|
|
801
|
+
### Image Sizes
|
|
802
|
+
|
|
803
|
+
**summary card:**
|
|
804
|
+
- **Recommended size:** 120×120 px (minimum)
|
|
805
|
+
- **Optimal size:** 280×150 px
|
|
806
|
+
- **Aspect ratio:** 1:1 (square)
|
|
807
|
+
|
|
808
|
+
**summary_large_image card:**
|
|
809
|
+
- **Minimum size:** 300×157 px
|
|
810
|
+
- **Optimal size:** 1200×628 px (like Open Graph)
|
|
811
|
+
- **Aspect ratio:** 2:1 or 1.91:1
|
|
812
|
+
- **Maximum file size:** 5 MB
|
|
813
|
+
- **Formats:** JPG, PNG, WebP, GIF
|
|
814
|
+
|
|
815
|
+
### Text Length
|
|
816
|
+
- **twitter:title:** up to 70 characters
|
|
817
|
+
- **twitter:description:** up to 200 characters
|
|
818
|
+
- **twitter:site:** @username format
|
|
819
|
+
- **twitter:creator:** @username format
|
|
820
|
+
|
|
821
|
+
### @username Attributes
|
|
822
|
+
- **twitter:site** — official Twitter account of the site/brand
|
|
823
|
+
- Shows as "via @username"
|
|
824
|
+
- Used for site attribution
|
|
825
|
+
- **twitter:creator** — Twitter account of the content author
|
|
826
|
+
- Shows as "by @username"
|
|
827
|
+
- Used for author attribution
|
|
828
|
+
|
|
829
|
+
### Testing
|
|
830
|
+
Check Twitter Card tags using:
|
|
831
|
+
- [Twitter Card Validator](https://cards-dev.twitter.com/validator) (legacy)
|
|
832
|
+
- Test by posting a test link on Twitter
|
|
833
|
+
- Use preview mode when creating a tweet
|
|
834
|
+
|
|
835
|
+
### Differences from Open Graph
|
|
836
|
+
Twitter Cards can use Open Graph tags as fallback:
|
|
837
|
+
- If `twitter:title` is missing, uses `og:title`
|
|
838
|
+
- If `twitter:description` is missing, uses `og:description`
|
|
839
|
+
- If `twitter:image` is missing, uses `og:image`
|
|
840
|
+
|
|
841
|
+
However, it's recommended to set Twitter Card tags explicitly for better control.
|
|
842
|
+
|
|
843
|
+
## Notes
|
|
844
|
+
|
|
845
|
+
- The class inherits all methods from `MetaManager`, including `html()`, `getItems()`, `setByList()`
|
|
846
|
+
- Automatically uses the `property` attribute (like Open Graph)
|
|
847
|
+
- All changes are immediately reflected in the DOM tree
|
|
848
|
+
- When creating an instance, existing Twitter Card tags from the page are automatically read
|
|
849
|
+
- For SSR, use the `html()` method to generate meta tags in the server template
|
|
850
|
+
- Content is automatically escaped to prevent XSS
|
|
851
|
+
- Twitter may cache cards, use Card Validator to clear cache
|
|
852
|
+
- Supports all official Twitter Card types
|
|
853
|
+
|