@cognigy/chat-components-vue 0.1.0
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/LICENSE +20 -0
- package/README.md +178 -0
- package/dist/assets/svg/ArrowBackIcon.vue.d.ts +9 -0
- package/dist/assets/svg/AudioPauseIcon.vue.d.ts +2 -0
- package/dist/assets/svg/AudioPlayIcon.vue.d.ts +2 -0
- package/dist/assets/svg/CloseIcon.vue.d.ts +2 -0
- package/dist/assets/svg/DownloadIcon.vue.d.ts +2 -0
- package/dist/assets/svg/VideoPlayIcon.vue.d.ts +9 -0
- package/dist/assets/svg/index.d.ts +7 -0
- package/dist/chat-components-vue.css +1 -0
- package/dist/chat-components-vue.js +9858 -0
- package/dist/components/Message.vue.d.ts +11 -0
- package/dist/components/common/ActionButton.vue.d.ts +59 -0
- package/dist/components/common/ActionButtons.vue.d.ts +36 -0
- package/dist/components/common/ChatBubble.vue.d.ts +22 -0
- package/dist/components/common/ChatEvent.vue.d.ts +20 -0
- package/dist/components/common/LinkIcon.vue.d.ts +2 -0
- package/dist/components/common/TypingIndicator.vue.d.ts +21 -0
- package/dist/components/common/Typography.vue.d.ts +38 -0
- package/dist/components/messages/AdaptiveCard.vue.d.ts +2 -0
- package/dist/components/messages/AudioMessage.vue.d.ts +5 -0
- package/dist/components/messages/DatePicker.vue.d.ts +2 -0
- package/dist/components/messages/FileMessage.vue.d.ts +2 -0
- package/dist/components/messages/Gallery.vue.d.ts +2 -0
- package/dist/components/messages/GalleryItem.vue.d.ts +7 -0
- package/dist/components/messages/ImageMessage.vue.d.ts +5 -0
- package/dist/components/messages/List.vue.d.ts +2 -0
- package/dist/components/messages/ListItem.vue.d.ts +16 -0
- package/dist/components/messages/TextMessage.vue.d.ts +15 -0
- package/dist/components/messages/TextWithButtons.vue.d.ts +2 -0
- package/dist/components/messages/VideoMessage.vue.d.ts +5 -0
- package/dist/composables/useChannelPayload.d.ts +47 -0
- package/dist/composables/useCollation.d.ts +47 -0
- package/dist/composables/useImageContext.d.ts +13 -0
- package/dist/composables/useMessageContext.d.ts +18 -0
- package/dist/composables/useSanitize.d.ts +8 -0
- package/dist/index.d.ts +33 -0
- package/dist/types/index.d.ts +275 -0
- package/dist/utils/helpers.d.ts +56 -0
- package/dist/utils/matcher.d.ts +20 -0
- package/dist/utils/sanitize.d.ts +28 -0
- package/dist/utils/theme.d.ts +18 -0
- package/package.json +94 -0
- package/src/assets/svg/ArrowBackIcon.vue +30 -0
- package/src/assets/svg/AudioPauseIcon.vue +20 -0
- package/src/assets/svg/AudioPlayIcon.vue +19 -0
- package/src/assets/svg/CloseIcon.vue +10 -0
- package/src/assets/svg/DownloadIcon.vue +10 -0
- package/src/assets/svg/VideoPlayIcon.vue +25 -0
- package/src/assets/svg/index.ts +7 -0
- package/src/components/Message.vue +152 -0
- package/src/components/common/ActionButton.vue +354 -0
- package/src/components/common/ActionButtons.vue +170 -0
- package/src/components/common/ChatBubble.vue +109 -0
- package/src/components/common/ChatEvent.vue +84 -0
- package/src/components/common/LinkIcon.vue +34 -0
- package/src/components/common/TypingIndicator.vue +202 -0
- package/src/components/common/Typography.vue +196 -0
- package/src/components/messages/AdaptiveCard.vue +292 -0
- package/src/components/messages/AudioMessage.vue +391 -0
- package/src/components/messages/DatePicker.vue +135 -0
- package/src/components/messages/FileMessage.vue +195 -0
- package/src/components/messages/Gallery.vue +296 -0
- package/src/components/messages/GalleryItem.vue +214 -0
- package/src/components/messages/ImageMessage.vue +368 -0
- package/src/components/messages/List.vue +149 -0
- package/src/components/messages/ListItem.vue +344 -0
- package/src/components/messages/TextMessage.vue +203 -0
- package/src/components/messages/TextWithButtons.vue +119 -0
- package/src/components/messages/VideoMessage.vue +343 -0
- package/src/composables/useChannelPayload.ts +101 -0
- package/src/composables/useCollation.ts +163 -0
- package/src/composables/useImageContext.ts +27 -0
- package/src/composables/useMessageContext.ts +41 -0
- package/src/composables/useSanitize.ts +25 -0
- package/src/index.ts +71 -0
- package/src/types/index.ts +373 -0
- package/src/utils/helpers.ts +164 -0
- package/src/utils/matcher.ts +283 -0
- package/src/utils/sanitize.ts +133 -0
- package/src/utils/theme.ts +58 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed, useCssModule, CSSProperties } from 'vue'
|
|
3
|
+
import { useMessageContext } from '../../composables/useMessageContext'
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
className?: string
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
10
|
+
className: '',
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
const styles = useCssModule()
|
|
14
|
+
const { message, config } = useMessageContext()
|
|
15
|
+
|
|
16
|
+
// Get direction mapping from config
|
|
17
|
+
const directionMapping = computed(() => config?.settings?.widgetSettings?.sourceDirectionMapping)
|
|
18
|
+
const disableBotOutputBorder = computed(() => config?.settings?.layout?.disableBotOutputBorder)
|
|
19
|
+
const botOutputMaxWidthPercentage = computed(() => config?.settings?.layout?.botOutputMaxWidthPercentage)
|
|
20
|
+
|
|
21
|
+
// Determine message type
|
|
22
|
+
const isUserMessage = computed(() => message.source === 'user')
|
|
23
|
+
const isBotMessage = computed(() => message.source === 'bot')
|
|
24
|
+
const isAgentMessage = computed(() => message.source === 'agent')
|
|
25
|
+
const isEngagementMessage = computed(() => message.source === 'engagement')
|
|
26
|
+
|
|
27
|
+
// Determine if border should be disabled
|
|
28
|
+
const disableBorder = computed(() =>
|
|
29
|
+
(isBotMessage.value || isEngagementMessage.value) && disableBotOutputBorder.value
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
// Get message direction based on source
|
|
33
|
+
const userMessageDirection = computed(() => directionMapping.value?.user || 'outgoing')
|
|
34
|
+
const botMessageDirection = computed(() => directionMapping.value?.bot || 'incoming')
|
|
35
|
+
const agentMessageDirection = computed(() => directionMapping.value?.agent || 'incoming')
|
|
36
|
+
|
|
37
|
+
// Compute CSS classes
|
|
38
|
+
const bubbleClasses = computed(() => {
|
|
39
|
+
const classes = [styles.bubble, 'chat-bubble']
|
|
40
|
+
|
|
41
|
+
if (props.className) classes.push(props.className)
|
|
42
|
+
if (isUserMessage.value) classes.push(styles[userMessageDirection.value])
|
|
43
|
+
if (isBotMessage.value) classes.push(styles[botMessageDirection.value])
|
|
44
|
+
if (isAgentMessage.value) classes.push(styles[agentMessageDirection.value])
|
|
45
|
+
if (disableBorder.value) classes.push(styles.disableBorder)
|
|
46
|
+
|
|
47
|
+
return classes
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
// Compute inline style
|
|
51
|
+
const bubbleStyle = computed((): CSSProperties => {
|
|
52
|
+
if ((isBotMessage.value || isEngagementMessage.value) && botOutputMaxWidthPercentage.value) {
|
|
53
|
+
return { maxWidth: `${botOutputMaxWidthPercentage.value}%` }
|
|
54
|
+
}
|
|
55
|
+
return {}
|
|
56
|
+
})
|
|
57
|
+
</script>
|
|
58
|
+
|
|
59
|
+
<template>
|
|
60
|
+
<div :class="bubbleClasses" :style="bubbleStyle">
|
|
61
|
+
<slot />
|
|
62
|
+
</div>
|
|
63
|
+
</template>
|
|
64
|
+
|
|
65
|
+
<style module>
|
|
66
|
+
.bubble {
|
|
67
|
+
border-radius: 15px;
|
|
68
|
+
border: 1px solid var(--cc-border-bot-message, transparent);
|
|
69
|
+
padding: 12px;
|
|
70
|
+
max-width: 295px;
|
|
71
|
+
width: max-content;
|
|
72
|
+
box-sizing: border-box;
|
|
73
|
+
margin-block-end: 12px;
|
|
74
|
+
word-break: break-word;
|
|
75
|
+
white-space: pre-wrap;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.bubble.disableBorder {
|
|
79
|
+
border: none;
|
|
80
|
+
background: none !important;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
article:global(.bot) .bubble {
|
|
84
|
+
background: var(--cc-background-bot-message, #f5f5f5);
|
|
85
|
+
color: var(--cc-bot-message-contrast-color, #1a1a1a);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
article:global(.agent) .bubble {
|
|
89
|
+
background: var(--cc-background-agent-message, #e8f4fd);
|
|
90
|
+
border-color: var(--cc-border-agent-message, transparent);
|
|
91
|
+
color: var(--cc-agent-message-contrast-color, #1a1a1a);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
article:global(.user) .bubble {
|
|
95
|
+
background: var(--cc-background-user-message, #1976d2);
|
|
96
|
+
border-color: var(--cc-border-user-message, transparent);
|
|
97
|
+
color: var(--cc-user-message-contrast-color, #ffffff);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
article:not(:global(.user)) .incoming,
|
|
101
|
+
article:global(.user) .incoming {
|
|
102
|
+
margin-inline-start: none;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
article:not(:global(.user)) .outgoing,
|
|
106
|
+
article:global(.user) .outgoing {
|
|
107
|
+
margin-inline-start: auto;
|
|
108
|
+
}
|
|
109
|
+
</style>
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
:id="id"
|
|
4
|
+
:class="eventClasses"
|
|
5
|
+
role="status"
|
|
6
|
+
aria-live="assertive"
|
|
7
|
+
>
|
|
8
|
+
<div :class="styles.eventPillTextWrapper">
|
|
9
|
+
<div :class="styles.eventText">
|
|
10
|
+
{{ text }}
|
|
11
|
+
</div>
|
|
12
|
+
</div>
|
|
13
|
+
</div>
|
|
14
|
+
</template>
|
|
15
|
+
|
|
16
|
+
<script setup lang="ts">
|
|
17
|
+
import { computed, useCssModule } from 'vue'
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* ChatEvent - Display chat event notifications
|
|
21
|
+
*
|
|
22
|
+
* Used for system messages like "Conversation started", "Agent joined", etc.
|
|
23
|
+
* These are informational messages that are not part of the conversation flow.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
interface Props {
|
|
27
|
+
/** Event text to display */
|
|
28
|
+
text?: string
|
|
29
|
+
/** Additional CSS class names */
|
|
30
|
+
className?: string
|
|
31
|
+
/** Element ID */
|
|
32
|
+
id?: string
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
36
|
+
text: '',
|
|
37
|
+
className: '',
|
|
38
|
+
id: undefined,
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
// Access CSS module classes
|
|
42
|
+
const styles = useCssModule()
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Compute combined class names
|
|
46
|
+
*/
|
|
47
|
+
const eventClasses = computed(() => {
|
|
48
|
+
const classes = [styles.eventPill]
|
|
49
|
+
|
|
50
|
+
if (props.className) {
|
|
51
|
+
classes.push(props.className)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return classes
|
|
55
|
+
})
|
|
56
|
+
</script>
|
|
57
|
+
|
|
58
|
+
<style module>
|
|
59
|
+
.eventPill {
|
|
60
|
+
display: flex;
|
|
61
|
+
max-width: 250px;
|
|
62
|
+
justify-content: center;
|
|
63
|
+
align-items: center;
|
|
64
|
+
gap: 10px;
|
|
65
|
+
margin-inline: auto;
|
|
66
|
+
margin-block-start: 40px;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.eventPillTextWrapper {
|
|
70
|
+
border-radius: 15px;
|
|
71
|
+
background: var(--cc-black-80, rgba(0, 0, 0, 0.8));
|
|
72
|
+
color: var(--cc-black-20, rgba(0, 0, 0, 0.2));
|
|
73
|
+
padding: 8px 12px;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.eventText {
|
|
77
|
+
/* Typography variant: title2-semibold equivalent */
|
|
78
|
+
font-family: 'Figtree', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
79
|
+
font-size: 14px;
|
|
80
|
+
font-weight: 600;
|
|
81
|
+
line-height: 1.4;
|
|
82
|
+
margin: 0;
|
|
83
|
+
}
|
|
84
|
+
</style>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// Simple SVG icon component for external links
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<template>
|
|
6
|
+
<svg
|
|
7
|
+
width="16"
|
|
8
|
+
height="16"
|
|
9
|
+
viewBox="0 0 16 16"
|
|
10
|
+
fill="none"
|
|
11
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
12
|
+
aria-hidden="true"
|
|
13
|
+
>
|
|
14
|
+
<g clip-path="url(#clip0_1309_801)">
|
|
15
|
+
<path
|
|
16
|
+
fill-rule="evenodd"
|
|
17
|
+
clip-rule="evenodd"
|
|
18
|
+
d="M14.0208 1C14.5731 1 15.0208 1.44772 15.0208 2L15.0208 11C15.0208 11.5523 14.5731 12 14.0208 12C13.4686 12 13.0208 11.5523 13.0208 11L13.0208 4.41421L2.70714 14.7279C2.31661 15.1184 1.68345 15.1184 1.29292 14.7279C0.902399 14.3374 0.902399 13.7042 1.29292 13.3137L11.6066 3L5.02085 3C4.46856 3 4.02085 2.55228 4.02085 2C4.02084 1.44771 4.46856 1 5.02085 1L14.0208 1Z"
|
|
19
|
+
fill="currentColor"
|
|
20
|
+
/>
|
|
21
|
+
</g>
|
|
22
|
+
<defs>
|
|
23
|
+
<clipPath id="clip0_1309_801">
|
|
24
|
+
<rect width="16" height="16" fill="white" />
|
|
25
|
+
</clipPath>
|
|
26
|
+
</defs>
|
|
27
|
+
</svg>
|
|
28
|
+
</template>
|
|
29
|
+
|
|
30
|
+
<style scoped>
|
|
31
|
+
svg {
|
|
32
|
+
flex-shrink: 0;
|
|
33
|
+
}
|
|
34
|
+
</style>
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
:class="indicatorClasses"
|
|
4
|
+
role="status"
|
|
5
|
+
aria-live="polite"
|
|
6
|
+
aria-label="Bot is typing"
|
|
7
|
+
>
|
|
8
|
+
<div :class="styles.dot"></div>
|
|
9
|
+
<div :class="styles.dot"></div>
|
|
10
|
+
<div :class="styles.dot"></div>
|
|
11
|
+
</div>
|
|
12
|
+
</template>
|
|
13
|
+
|
|
14
|
+
<script setup lang="ts">
|
|
15
|
+
import { computed, useCssModule } from 'vue'
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* TypingIndicator - Animated typing indicator
|
|
19
|
+
*
|
|
20
|
+
* Displays three bouncing dots to indicate that bot or agent is typing.
|
|
21
|
+
* Purely presentational - parent components control visibility via v-if.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
type SourceDirection = 'incoming' | 'outgoing'
|
|
25
|
+
|
|
26
|
+
interface Props {
|
|
27
|
+
/** Additional CSS class names */
|
|
28
|
+
className?: string
|
|
29
|
+
/** Direction of the indicator (incoming: left-aligned, outgoing: right-aligned) */
|
|
30
|
+
direction?: SourceDirection
|
|
31
|
+
/** Remove border styling */
|
|
32
|
+
disableBorder?: boolean
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
36
|
+
className: '',
|
|
37
|
+
direction: 'incoming',
|
|
38
|
+
disableBorder: false,
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
// Access CSS module classes
|
|
42
|
+
const styles = useCssModule()
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Compute combined class names
|
|
46
|
+
*/
|
|
47
|
+
const indicatorClasses = computed(() => {
|
|
48
|
+
const classes = [
|
|
49
|
+
styles.typingIndicator,
|
|
50
|
+
'webchat-typing-indicator', // Global class for external styling
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
if (props.className) {
|
|
54
|
+
classes.push(props.className)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (props.direction) {
|
|
58
|
+
classes.push(styles[props.direction])
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (props.disableBorder) {
|
|
62
|
+
classes.push(styles.disableBorder)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return classes
|
|
66
|
+
})
|
|
67
|
+
</script>
|
|
68
|
+
|
|
69
|
+
<style module>
|
|
70
|
+
.typingIndicator {
|
|
71
|
+
align-items: center;
|
|
72
|
+
background-color: var(--cc-white, #ffffff);
|
|
73
|
+
border-radius: 15px;
|
|
74
|
+
border: 1px solid var(--cc-black-80, rgba(0, 0, 0, 0.8));
|
|
75
|
+
box-sizing: border-box;
|
|
76
|
+
display: flex;
|
|
77
|
+
gap: 6px;
|
|
78
|
+
height: 44px;
|
|
79
|
+
justify-content: center;
|
|
80
|
+
padding-block: 10px;
|
|
81
|
+
padding-inline: 10px;
|
|
82
|
+
width: 62px;
|
|
83
|
+
margin-block: var(--webchat-message-margin-block, 24px);
|
|
84
|
+
margin-inline: var(--webchat-message-margin-inline, 20px);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.typingIndicator.disableBorder {
|
|
88
|
+
border: none;
|
|
89
|
+
background: none !important;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.incoming {
|
|
93
|
+
margin-inline-start: none;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.outgoing {
|
|
97
|
+
margin-inline-start: auto;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.dot {
|
|
101
|
+
background: var(--cc-black-40, rgba(0, 0, 0, 0.4));
|
|
102
|
+
border-radius: 999px;
|
|
103
|
+
width: 6px;
|
|
104
|
+
height: 6px;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.dot:nth-child(1),
|
|
108
|
+
.dot:nth-child(2),
|
|
109
|
+
.dot:nth-child(3) {
|
|
110
|
+
animation-duration: 1.2s;
|
|
111
|
+
animation-timing-function: ease-in-out;
|
|
112
|
+
animation-fill-mode: both;
|
|
113
|
+
animation-iteration-count: infinite;
|
|
114
|
+
animation-delay: -1000ms;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.dot:nth-child(1) {
|
|
118
|
+
animation-name: dot1;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.dot:nth-child(2) {
|
|
122
|
+
animation-name: dot2;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.dot:nth-child(3) {
|
|
126
|
+
animation-name: dot3;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/* Respect user's motion preferences */
|
|
130
|
+
@media (prefers-reduced-motion) {
|
|
131
|
+
.dot {
|
|
132
|
+
animation: none !important;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/* Animation keyframes for sequential bouncing effect */
|
|
137
|
+
@keyframes dot1 {
|
|
138
|
+
0% {
|
|
139
|
+
transform: translateY(0);
|
|
140
|
+
}
|
|
141
|
+
10% {
|
|
142
|
+
transform: translateY(-3px);
|
|
143
|
+
}
|
|
144
|
+
20% {
|
|
145
|
+
transform: translateY(-6px);
|
|
146
|
+
}
|
|
147
|
+
30% {
|
|
148
|
+
transform: translateY(-3px);
|
|
149
|
+
}
|
|
150
|
+
40% {
|
|
151
|
+
transform: translateY(0);
|
|
152
|
+
}
|
|
153
|
+
50% {
|
|
154
|
+
transform: translateY(0);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
@keyframes dot2 {
|
|
159
|
+
0% {
|
|
160
|
+
transform: translateY(0);
|
|
161
|
+
}
|
|
162
|
+
10% {
|
|
163
|
+
transform: translateY(0);
|
|
164
|
+
}
|
|
165
|
+
20% {
|
|
166
|
+
transform: translateY(-3px);
|
|
167
|
+
}
|
|
168
|
+
30% {
|
|
169
|
+
transform: translateY(-6px);
|
|
170
|
+
}
|
|
171
|
+
40% {
|
|
172
|
+
transform: translateY(-3px);
|
|
173
|
+
}
|
|
174
|
+
50% {
|
|
175
|
+
transform: translateY(0);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
@keyframes dot3 {
|
|
180
|
+
0% {
|
|
181
|
+
transform: translateY(0);
|
|
182
|
+
}
|
|
183
|
+
10% {
|
|
184
|
+
transform: translateY(0);
|
|
185
|
+
}
|
|
186
|
+
20% {
|
|
187
|
+
transform: translateY(0);
|
|
188
|
+
}
|
|
189
|
+
30% {
|
|
190
|
+
transform: translateY(-3px);
|
|
191
|
+
}
|
|
192
|
+
40% {
|
|
193
|
+
transform: translateY(-6px);
|
|
194
|
+
}
|
|
195
|
+
50% {
|
|
196
|
+
transform: translateY(-3px);
|
|
197
|
+
}
|
|
198
|
+
65% {
|
|
199
|
+
transform: translateY(0);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
</style>
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed, useCssModule, useAttrs, CSSProperties } from 'vue'
|
|
3
|
+
|
|
4
|
+
export type TagVariant =
|
|
5
|
+
| 'h1-semibold'
|
|
6
|
+
| 'h2-regular'
|
|
7
|
+
| 'h2-semibold'
|
|
8
|
+
| 'title1-semibold'
|
|
9
|
+
| 'title1-regular'
|
|
10
|
+
| 'title2-semibold'
|
|
11
|
+
| 'title2-regular'
|
|
12
|
+
| 'body-regular'
|
|
13
|
+
| 'body-semibold'
|
|
14
|
+
| 'copy-medium'
|
|
15
|
+
| 'cta-semibold'
|
|
16
|
+
|
|
17
|
+
type ColorVariant = 'primary' | 'secondary'
|
|
18
|
+
|
|
19
|
+
interface Props {
|
|
20
|
+
variant?: TagVariant
|
|
21
|
+
component?: string
|
|
22
|
+
className?: string
|
|
23
|
+
style?: CSSProperties
|
|
24
|
+
color?: string
|
|
25
|
+
id?: string
|
|
26
|
+
ariaHidden?: boolean
|
|
27
|
+
tabIndex?: number
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
31
|
+
variant: 'body-regular',
|
|
32
|
+
component: undefined,
|
|
33
|
+
className: '',
|
|
34
|
+
style: undefined,
|
|
35
|
+
color: undefined,
|
|
36
|
+
id: undefined,
|
|
37
|
+
ariaHidden: undefined,
|
|
38
|
+
tabIndex: undefined,
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
const attrs = useAttrs()
|
|
42
|
+
const styles = useCssModule()
|
|
43
|
+
|
|
44
|
+
// Mapping between variants and default HTML tags
|
|
45
|
+
const variantsMapping: Record<TagVariant, string> = {
|
|
46
|
+
'h1-semibold': 'h1',
|
|
47
|
+
'h2-regular': 'h2',
|
|
48
|
+
'h2-semibold': 'h2',
|
|
49
|
+
'title1-semibold': 'h3',
|
|
50
|
+
'title1-regular': 'h4',
|
|
51
|
+
'title2-semibold': 'h5',
|
|
52
|
+
'title2-regular': 'h6',
|
|
53
|
+
'body-semibold': 'p',
|
|
54
|
+
'body-regular': 'p',
|
|
55
|
+
'copy-medium': 'p',
|
|
56
|
+
'cta-semibold': 'p',
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Color mapping for predefined colors
|
|
60
|
+
const colorsMapping: Record<ColorVariant, string> = {
|
|
61
|
+
primary: 'var(--cc-primary-color)',
|
|
62
|
+
secondary: 'var(--cc-secondary-color)',
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Determine which HTML element to render
|
|
66
|
+
const componentTag = computed(() => {
|
|
67
|
+
return props.component ?? variantsMapping[props.variant]
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
// Compute the color value
|
|
71
|
+
const typographyColor = computed(() => {
|
|
72
|
+
if (!props.color) return undefined
|
|
73
|
+
return colorsMapping[props.color as ColorVariant] ?? props.color
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
// Compute CSS classes
|
|
77
|
+
const componentClasses = computed(() => {
|
|
78
|
+
const classes = [styles[props.variant]]
|
|
79
|
+
if (props.className) classes.push(props.className)
|
|
80
|
+
if (props.color) classes.push(props.color)
|
|
81
|
+
return classes.filter(Boolean).join(' ')
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
// Compute merged styles
|
|
85
|
+
const componentStyle = computed(() => {
|
|
86
|
+
const mergedStyle: CSSProperties = { ...props.style }
|
|
87
|
+
if (typographyColor.value) {
|
|
88
|
+
mergedStyle.color = typographyColor.value
|
|
89
|
+
}
|
|
90
|
+
return mergedStyle
|
|
91
|
+
})
|
|
92
|
+
</script>
|
|
93
|
+
|
|
94
|
+
<template>
|
|
95
|
+
<component
|
|
96
|
+
:is="componentTag"
|
|
97
|
+
:id="id"
|
|
98
|
+
:class="componentClasses"
|
|
99
|
+
:style="componentStyle"
|
|
100
|
+
:aria-hidden="ariaHidden"
|
|
101
|
+
:tabIndex="tabIndex"
|
|
102
|
+
v-bind="attrs"
|
|
103
|
+
>
|
|
104
|
+
<slot />
|
|
105
|
+
</component>
|
|
106
|
+
</template>
|
|
107
|
+
|
|
108
|
+
<style module>
|
|
109
|
+
.h1-semibold {
|
|
110
|
+
font-family: Figtree;
|
|
111
|
+
font-size: 2.125rem; /* 34px */
|
|
112
|
+
font-style: normal;
|
|
113
|
+
font-weight: 600;
|
|
114
|
+
line-height: 2.55rem; /* 40.8px */
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.h2-regular {
|
|
118
|
+
font-family: Figtree;
|
|
119
|
+
font-size: 1.125rem; /* 18px */
|
|
120
|
+
font-style: normal;
|
|
121
|
+
font-weight: 400;
|
|
122
|
+
line-height: 1.4625rem; /* 23.4px */
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.h2-semibold {
|
|
126
|
+
font-family: Figtree;
|
|
127
|
+
font-size: 1.125rem; /* 18px */
|
|
128
|
+
font-style: normal;
|
|
129
|
+
font-weight: 600;
|
|
130
|
+
line-height: 1.4625rem; /* 23.4px */
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.title1-regular {
|
|
134
|
+
font-family: Figtree;
|
|
135
|
+
font-size: 1rem; /* 16px */
|
|
136
|
+
font-style: normal;
|
|
137
|
+
font-weight: 400;
|
|
138
|
+
line-height: 1.4rem; /* 22.4px */
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.title1-semibold {
|
|
142
|
+
font-family: Figtree;
|
|
143
|
+
font-size: 1rem; /* 16px */
|
|
144
|
+
font-style: normal;
|
|
145
|
+
font-weight: 600;
|
|
146
|
+
line-height: 1.4rem; /* 22.4px */
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.title2-regular {
|
|
150
|
+
font-family: Figtree;
|
|
151
|
+
font-size: 0.75rem; /* 12px */
|
|
152
|
+
font-style: normal;
|
|
153
|
+
font-weight: 400;
|
|
154
|
+
line-height: 1.05rem; /* 16.8px */
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.title2-semibold {
|
|
158
|
+
font-family: Figtree;
|
|
159
|
+
font-size: 0.75rem; /* 12px */
|
|
160
|
+
font-style: normal;
|
|
161
|
+
font-weight: 600;
|
|
162
|
+
line-height: 0.975rem; /* 15.6px */
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.body-regular {
|
|
166
|
+
font-family: Figtree;
|
|
167
|
+
font-size: 0.875rem; /* 14px */
|
|
168
|
+
font-style: normal;
|
|
169
|
+
font-weight: 400;
|
|
170
|
+
line-height: 1.225rem; /* 19.6px */
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.body-semibold {
|
|
174
|
+
font-family: Figtree;
|
|
175
|
+
font-size: 0.875rem; /* 14px */
|
|
176
|
+
font-style: normal;
|
|
177
|
+
font-weight: 600;
|
|
178
|
+
line-height: 1.1375rem; /* 18.2px */
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.copy-medium {
|
|
182
|
+
font-family: Figtree;
|
|
183
|
+
font-size: 0.75rem; /* 12px */
|
|
184
|
+
font-style: normal;
|
|
185
|
+
font-weight: 500;
|
|
186
|
+
line-height: 1.05rem; /* 16.8px */
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.cta-semibold {
|
|
190
|
+
font-family: Figtree;
|
|
191
|
+
font-size: 0.875rem; /* 14px */
|
|
192
|
+
font-style: normal;
|
|
193
|
+
font-weight: 600;
|
|
194
|
+
line-height: 1.1375rem; /* 18.2px */
|
|
195
|
+
}
|
|
196
|
+
</style>
|