@todovue/tv-footer 1.1.0 → 1.1.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/CHANGELOG.md +67 -0
- package/package.json +14 -9
- package/src/assets/scss/_variables.scss +12 -0
- package/src/assets/scss/style.scss +396 -0
- package/src/components/TvFooter.vue +101 -0
- package/src/composables/useFooter.js +45 -0
- package/src/demo/Demo.vue +21 -0
- package/src/demo/utils/demos/default.vue +68 -0
- package/src/demo/utils/demos/newsletter.vue +79 -0
- package/src/demo/utils/icons/facebook.png +0 -0
- package/src/demo/utils/icons/github-white.svg +1 -0
- package/src/demo/utils/icons/github.svg +1 -0
- package/src/demo/utils/icons/todovue.png +0 -0
- package/src/demo/utils/mocks.js +94 -0
- package/src/entry.ts +21 -0
- package/src/main.js +7 -0
- package/src/style.scss +1 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `@todovue/tv-footer` will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [1.1.1] - 2026-01-27
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
- Simplified the file list in `package.json` to include only essential assets.
|
|
12
|
+
- Simplified the build configuration by removing demo-specific logic.
|
|
13
|
+
- Enhanced GitHub Actions workflows to automate npm package publishing and GitHub release creation.
|
|
14
|
+
- Moved the `@todovue/tv-demo` component import from main.js to `Demo.vue` to localize its usage.
|
|
15
|
+
- Updated build commands to include `README.md` and `CHANGELOG.md` files in the public directory during the build process.
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
- Included the `src` directory in the `package.json` files list to ensure component source files are bundled in the package distribution.
|
|
19
|
+
|
|
20
|
+
### Removed
|
|
21
|
+
- Eliminated the global import of the `@todovue/tv-demo` component from `main.js`.
|
|
22
|
+
|
|
23
|
+
### Dependencies
|
|
24
|
+
- Updated `@todovue/tv-demo` to `^1.4.11`.
|
|
25
|
+
- Updated `vue` to `^3.5.27`.
|
|
26
|
+
- Updated `sass` to `^1.97.3`.
|
|
27
|
+
|
|
28
|
+
## [1.1.0] - 2026-01-21
|
|
29
|
+
|
|
30
|
+
### Added
|
|
31
|
+
- Added `newsletter` properties to `useFooter` composable return.
|
|
32
|
+
- Added new styles for newsletter form and back-to-top button.
|
|
33
|
+
|
|
34
|
+
### Dependencies
|
|
35
|
+
- Updated `@todovue/tv-demo` to `^1.4.4`.
|
|
36
|
+
- Updated `sass` to `^1.97.2`.
|
|
37
|
+
- Updated `vite` to `^7.3.1`.
|
|
38
|
+
|
|
39
|
+
### Changed
|
|
40
|
+
- Enhanced footer styles to improve visual consistency across the application.
|
|
41
|
+
- Improved responsiveness of the footer component for better mobile and tablet compatibility.
|
|
42
|
+
|
|
43
|
+
## [1.0.0] - 2026-01-07
|
|
44
|
+
|
|
45
|
+
### Added
|
|
46
|
+
- Initial release of TvFooter component
|
|
47
|
+
- Responsive grid layout (1 column mobile, 2 tablet, 4 desktop)
|
|
48
|
+
- Brand section with logo and name display support
|
|
49
|
+
- Version display capability
|
|
50
|
+
- Multiple navigation sections with titles and links
|
|
51
|
+
- Social media links with icon support (iconUrl and icon class)
|
|
52
|
+
- Legal links section (Privacy, Terms, etc.)
|
|
53
|
+
- Dynamic copyright with automatic year replacement (`{year}` placeholder)
|
|
54
|
+
- Light/Dark mode support built-in
|
|
55
|
+
- SSR-safe implementation (Nuxt 3 compatible)
|
|
56
|
+
- `useFooter` composable for custom implementations
|
|
57
|
+
- TypeScript support with type definitions
|
|
58
|
+
- Semantic HTML structure for accessibility
|
|
59
|
+
- ARIA labels and proper link attributes
|
|
60
|
+
- External link safety (`rel="noopener noreferrer"`)
|
|
61
|
+
- Smooth hover effects and transitions
|
|
62
|
+
- Backdrop blur effect on social icons
|
|
63
|
+
- Comprehensive documentation and examples
|
|
64
|
+
|
|
65
|
+
[1.1.1]: https://github.com/TODOvue/tv-footer/pull/3/files
|
|
66
|
+
[1.1.0]: https://github.com/TODOvue/tv-footer/pull/2/files
|
|
67
|
+
[1.0.0]: https://github.com/TODOvue/tv-footer/pull/1/files
|
package/package.json
CHANGED
|
@@ -4,10 +4,11 @@
|
|
|
4
4
|
"author": "Cristhian Daza",
|
|
5
5
|
"description": "A simple and customizable footer component for Vue.js applications, perfect for enhancing your web projects with ease.",
|
|
6
6
|
"license": "MIT",
|
|
7
|
-
"version": "1.1.
|
|
7
|
+
"version": "1.1.1",
|
|
8
8
|
"type": "module",
|
|
9
9
|
"homepage": "https://ui.todovue.blog/footer",
|
|
10
10
|
"repository": {
|
|
11
|
+
"name": "@todovue/tv-footer",
|
|
11
12
|
"type": "git",
|
|
12
13
|
"url": "git+https://github.com/TODOvue/tv-footer.git"
|
|
13
14
|
},
|
|
@@ -32,16 +33,21 @@
|
|
|
32
33
|
"require": "./dist/tv-footer.cjs.js"
|
|
33
34
|
},
|
|
34
35
|
"./style.css": "./dist/tv-footer.css",
|
|
35
|
-
"./nuxt": "./nuxt.js"
|
|
36
|
+
"./nuxt": "./nuxt.js",
|
|
37
|
+
"./demo": {
|
|
38
|
+
"import": "./src/demo/Demo.vue"
|
|
39
|
+
}
|
|
36
40
|
},
|
|
37
41
|
"main": "dist/tv-footer.cjs.js",
|
|
38
42
|
"module": "dist/tv-footer.es.js",
|
|
39
43
|
"types": "dist/tv-footer.d.ts",
|
|
40
44
|
"files": [
|
|
41
|
-
"
|
|
45
|
+
"CHANGELOG.md",
|
|
42
46
|
"LICENSE",
|
|
43
47
|
"README.md",
|
|
44
|
-
"
|
|
48
|
+
"dist",
|
|
49
|
+
"nuxt.js",
|
|
50
|
+
"src"
|
|
45
51
|
],
|
|
46
52
|
"engines": {
|
|
47
53
|
"node": ">=20.19.0"
|
|
@@ -53,16 +59,15 @@
|
|
|
53
59
|
],
|
|
54
60
|
"scripts": {
|
|
55
61
|
"dev": "vite",
|
|
56
|
-
"build": "vite build"
|
|
57
|
-
"build:demo": "cp README.md public/ && cp CHANGELOG.md public/ && VITE_BUILD_TARGET=demo vite build"
|
|
62
|
+
"build": "vite build"
|
|
58
63
|
},
|
|
59
64
|
"peerDependencies": {
|
|
60
|
-
"vue": "^3.5.
|
|
65
|
+
"vue": "^3.5.27"
|
|
61
66
|
},
|
|
62
67
|
"devDependencies": {
|
|
63
|
-
"@todovue/tv-demo": "^1.4.
|
|
68
|
+
"@todovue/tv-demo": "^1.4.11",
|
|
64
69
|
"@vitejs/plugin-vue": "^6.0.3",
|
|
65
|
-
"sass": "^1.97.
|
|
70
|
+
"sass": "^1.97.3",
|
|
66
71
|
"vite": "^7.3.1",
|
|
67
72
|
"vite-plugin-dts": "^4.5.4"
|
|
68
73
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/* 🌙 Dark Theme */
|
|
2
|
+
$dark-body-bg: #161E31;
|
|
3
|
+
$dark-card-bg: #0E131F;
|
|
4
|
+
$dark-text: #CBD5E1;
|
|
5
|
+
|
|
6
|
+
/* ☀️ Light Theme */
|
|
7
|
+
$light-body-bg: #f8FAFC;
|
|
8
|
+
$light-card-bg: #B9C4DF;
|
|
9
|
+
$light-button-text: #F1F9F9;
|
|
10
|
+
$light-text: #1E293B;
|
|
11
|
+
|
|
12
|
+
$primary-color: #ef233c;
|
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
@use "variables" as *;
|
|
2
|
+
@use "sass:color";
|
|
3
|
+
|
|
4
|
+
.tv-footer {
|
|
5
|
+
width: 100%;
|
|
6
|
+
padding: 3rem 1.5rem;
|
|
7
|
+
box-sizing: border-box;
|
|
8
|
+
transition: background-color 0.4s ease, color 0.4s ease;
|
|
9
|
+
position: relative;
|
|
10
|
+
|
|
11
|
+
background-color: $dark-card-bg;
|
|
12
|
+
color: $dark-text;
|
|
13
|
+
border-top: 1px solid rgba($dark-text, 0.05);
|
|
14
|
+
|
|
15
|
+
&,
|
|
16
|
+
.dark-mode & {
|
|
17
|
+
|
|
18
|
+
.tv-footer__logo,
|
|
19
|
+
.tv-footer__section-title {
|
|
20
|
+
color: #fff;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.tv-footer__social-link {
|
|
24
|
+
background-color: rgba(255, 255, 255, 0.03);
|
|
25
|
+
color: inherit;
|
|
26
|
+
border: 1px solid rgba(255, 255, 255, 0.05);
|
|
27
|
+
|
|
28
|
+
&:hover {
|
|
29
|
+
background-color: $primary-color;
|
|
30
|
+
border-color: $primary-color;
|
|
31
|
+
color: white;
|
|
32
|
+
box-shadow: 0 0 15px rgba($primary-color, 0.4);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.light-mode & {
|
|
38
|
+
background-color: $light-card-bg;
|
|
39
|
+
color: $light-text;
|
|
40
|
+
border-top-color: rgba($light-text, 0.05);
|
|
41
|
+
|
|
42
|
+
.tv-footer__logo,
|
|
43
|
+
.tv-footer__section-title {
|
|
44
|
+
color: $light-text;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.tv-footer__social-link {
|
|
48
|
+
background-color: rgba(255, 255, 255, 0.5);
|
|
49
|
+
color: inherit;
|
|
50
|
+
border: 1px solid rgba(0, 0, 0, 0.05);
|
|
51
|
+
backdrop-filter: blur(4px);
|
|
52
|
+
|
|
53
|
+
&:hover {
|
|
54
|
+
background-color: $primary-color;
|
|
55
|
+
border-color: $primary-color;
|
|
56
|
+
color: $light-button-text;
|
|
57
|
+
box-shadow: 0 4px 12px rgba($primary-color, 0.3);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.tv-footer__link:hover {
|
|
62
|
+
color: $primary-color;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.tv-footer__newsletter-input {
|
|
66
|
+
background-color: rgba(255, 255, 255, 0.6);
|
|
67
|
+
border-color: rgba(0, 0, 0, 0.05);
|
|
68
|
+
color: $light-text;
|
|
69
|
+
backdrop-filter: blur(8px);
|
|
70
|
+
|
|
71
|
+
&:focus {
|
|
72
|
+
background-color: rgba(255, 255, 255, 0.9);
|
|
73
|
+
border-color: $primary-color;
|
|
74
|
+
box-shadow: 0 0 0 4px rgba($primary-color, 0.1);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.tv-footer__container {
|
|
81
|
+
display: flex;
|
|
82
|
+
flex-wrap: wrap;
|
|
83
|
+
gap: 4rem;
|
|
84
|
+
max-width: 1200px;
|
|
85
|
+
margin: 0 auto;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.tv-footer__left-column {
|
|
89
|
+
flex: 1 1 250px;
|
|
90
|
+
max-width: 400px;
|
|
91
|
+
display: flex;
|
|
92
|
+
flex-direction: column;
|
|
93
|
+
gap: 2rem;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.tv-footer__brand {
|
|
97
|
+
display: flex;
|
|
98
|
+
flex-direction: column;
|
|
99
|
+
gap: 1rem;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.tv-footer__logo {
|
|
103
|
+
font-weight: 800;
|
|
104
|
+
font-size: 1.5rem;
|
|
105
|
+
text-decoration: none;
|
|
106
|
+
display: inline-flex;
|
|
107
|
+
align-items: center;
|
|
108
|
+
gap: 0.75rem;
|
|
109
|
+
letter-spacing: -0.02em;
|
|
110
|
+
|
|
111
|
+
img {
|
|
112
|
+
height: 100px;
|
|
113
|
+
width: auto;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.tv-footer__version {
|
|
118
|
+
font-size: 0.75rem;
|
|
119
|
+
opacity: 0.6;
|
|
120
|
+
font-weight: 500;
|
|
121
|
+
background: rgba(127, 127, 127, 0.1);
|
|
122
|
+
padding: 0.125rem 0.5rem;
|
|
123
|
+
border-radius: 12px;
|
|
124
|
+
align-self: flex-start;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.tv-footer__nav-definitions {
|
|
128
|
+
display: flex;
|
|
129
|
+
flex: 999 1 400px;
|
|
130
|
+
flex-wrap: wrap;
|
|
131
|
+
gap: 3rem;
|
|
132
|
+
|
|
133
|
+
@media (max-width: 768px) {
|
|
134
|
+
gap: 2rem;
|
|
135
|
+
flex-basis: 100%;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.tv-footer__section {
|
|
140
|
+
min-width: 140px;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.tv-footer__section-title {
|
|
144
|
+
font-weight: 700;
|
|
145
|
+
margin-bottom: 1.25rem;
|
|
146
|
+
font-size: 0.9rem;
|
|
147
|
+
letter-spacing: 0.05em;
|
|
148
|
+
text-transform: uppercase;
|
|
149
|
+
opacity: 1;
|
|
150
|
+
position: relative;
|
|
151
|
+
|
|
152
|
+
&::after {
|
|
153
|
+
content: '';
|
|
154
|
+
display: block;
|
|
155
|
+
width: 20px;
|
|
156
|
+
height: 2px;
|
|
157
|
+
background-color: $primary-color;
|
|
158
|
+
margin-top: 0.5rem;
|
|
159
|
+
border-radius: 2px;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
.tv-footer__links {
|
|
164
|
+
list-style: none;
|
|
165
|
+
padding: 0;
|
|
166
|
+
margin: 0;
|
|
167
|
+
display: flex;
|
|
168
|
+
flex-direction: column;
|
|
169
|
+
gap: 0.875rem;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.tv-footer__link {
|
|
173
|
+
color: inherit;
|
|
174
|
+
text-decoration: none;
|
|
175
|
+
font-size: 0.95rem;
|
|
176
|
+
transition: all 0.2s ease;
|
|
177
|
+
cursor: pointer;
|
|
178
|
+
display: flex;
|
|
179
|
+
align-items: center;
|
|
180
|
+
gap: 0.5rem;
|
|
181
|
+
opacity: 0.75;
|
|
182
|
+
width: fit-content;
|
|
183
|
+
|
|
184
|
+
&:hover {
|
|
185
|
+
color: $primary-color;
|
|
186
|
+
opacity: 1;
|
|
187
|
+
transform: translateX(4px);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
&::before {
|
|
191
|
+
content: '';
|
|
192
|
+
display: block;
|
|
193
|
+
width: 4px;
|
|
194
|
+
height: 4px;
|
|
195
|
+
background-color: currentColor;
|
|
196
|
+
border-radius: 50%;
|
|
197
|
+
opacity: 0;
|
|
198
|
+
transition: opacity 0.2s;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
&:hover::before {
|
|
202
|
+
opacity: 1;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
.tv-footer__social-section {
|
|
207
|
+
display: flex;
|
|
208
|
+
flex-direction: column;
|
|
209
|
+
justify-content: flex-start;
|
|
210
|
+
padding-top: 0.5rem;
|
|
211
|
+
flex: 0 0 auto;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
.tv-footer__social {
|
|
215
|
+
display: flex;
|
|
216
|
+
gap: 0.75rem;
|
|
217
|
+
flex-wrap: wrap;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
.tv-footer__social-link {
|
|
221
|
+
font-size: 1.1rem;
|
|
222
|
+
text-decoration: none;
|
|
223
|
+
display: flex;
|
|
224
|
+
align-items: center;
|
|
225
|
+
justify-content: center;
|
|
226
|
+
width: 2.5rem;
|
|
227
|
+
height: 2.5rem;
|
|
228
|
+
border-radius: 50%;
|
|
229
|
+
transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
|
230
|
+
|
|
231
|
+
&:hover {
|
|
232
|
+
transform: translateY(-4px) scale(1.1);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
img {
|
|
236
|
+
width: 1.25rem;
|
|
237
|
+
height: 1.25rem;
|
|
238
|
+
object-fit: contain;
|
|
239
|
+
display: block;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
.tv-footer__bottom {
|
|
244
|
+
border-top: 1px solid rgba(127, 127, 127, 0.1);
|
|
245
|
+
margin-top: 3rem;
|
|
246
|
+
padding-top: 1.5rem;
|
|
247
|
+
text-align: center;
|
|
248
|
+
font-size: 0.85rem;
|
|
249
|
+
display: flex;
|
|
250
|
+
flex-direction: column;
|
|
251
|
+
align-items: center;
|
|
252
|
+
gap: 1rem;
|
|
253
|
+
max-width: 1200px;
|
|
254
|
+
margin-left: auto;
|
|
255
|
+
margin-right: auto;
|
|
256
|
+
opacity: 0.7;
|
|
257
|
+
|
|
258
|
+
@media (min-width: 640px) {
|
|
259
|
+
flex-direction: row;
|
|
260
|
+
justify-content: space-between;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
.tv-footer__links {
|
|
264
|
+
flex-direction: row;
|
|
265
|
+
gap: 1.5rem;
|
|
266
|
+
flex-wrap: wrap;
|
|
267
|
+
justify-content: center;
|
|
268
|
+
|
|
269
|
+
li {
|
|
270
|
+
display: inline-block;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
.tv-footer__link {
|
|
274
|
+
font-size: 0.8rem;
|
|
275
|
+
|
|
276
|
+
&:hover {
|
|
277
|
+
transform: none;
|
|
278
|
+
text-decoration: underline;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
&::before {
|
|
282
|
+
display: none;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
.tv-footer__newsletter {
|
|
289
|
+
display: flex;
|
|
290
|
+
flex-direction: column;
|
|
291
|
+
gap: 1rem;
|
|
292
|
+
margin-top: 1rem;
|
|
293
|
+
width: 100%;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
.tv-footer__newsletter-title {
|
|
297
|
+
font-weight: 700;
|
|
298
|
+
font-size: 1rem;
|
|
299
|
+
margin: 0;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
.tv-footer__newsletter-text {
|
|
303
|
+
font-size: 0.9rem;
|
|
304
|
+
line-height: 1.5;
|
|
305
|
+
opacity: 0.8;
|
|
306
|
+
margin: 0;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
.tv-footer__newsletter-form {
|
|
310
|
+
display: flex;
|
|
311
|
+
gap: 0.5rem;
|
|
312
|
+
flex-direction: column;
|
|
313
|
+
|
|
314
|
+
@media(min-width: 400px) {
|
|
315
|
+
flex-direction: row;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
.tv-footer__newsletter-input {
|
|
320
|
+
flex: 1;
|
|
321
|
+
min-width: 0;
|
|
322
|
+
padding: 0.75rem 1.25rem;
|
|
323
|
+
border-radius: 9999px;
|
|
324
|
+
border: 1px solid transparent;
|
|
325
|
+
background-color: rgba(255, 255, 255, 0.05);
|
|
326
|
+
color: inherit;
|
|
327
|
+
font-size: 0.9rem;
|
|
328
|
+
outline: none;
|
|
329
|
+
transition: all 0.3s ease;
|
|
330
|
+
|
|
331
|
+
&:focus {
|
|
332
|
+
background-color: rgba(255, 255, 255, 0.1);
|
|
333
|
+
border-color: $primary-color;
|
|
334
|
+
box-shadow: 0 0 0 4px rgba($primary-color, 0.2);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
&::placeholder {
|
|
338
|
+
opacity: 0.5;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
.tv-footer__newsletter-button {
|
|
343
|
+
padding: 0.75rem 1.5rem;
|
|
344
|
+
border-radius: 9999px;
|
|
345
|
+
border: none;
|
|
346
|
+
background-color: $primary-color;
|
|
347
|
+
color: #fff;
|
|
348
|
+
font-weight: 600;
|
|
349
|
+
font-size: 0.9rem;
|
|
350
|
+
cursor: pointer;
|
|
351
|
+
transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
|
352
|
+
display: inline-flex;
|
|
353
|
+
align-items: center;
|
|
354
|
+
justify-content: center;
|
|
355
|
+
white-space: nowrap;
|
|
356
|
+
|
|
357
|
+
&:hover {
|
|
358
|
+
background-color: color.adjust($primary-color, $lightness: -5%);
|
|
359
|
+
transform: translateY(-2px);
|
|
360
|
+
box-shadow: 0 4px 12px rgba($primary-color, 0.4);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
&:active {
|
|
364
|
+
transform: translateY(0);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
.tv-footer__back-to-top {
|
|
369
|
+
position: fixed;
|
|
370
|
+
bottom: 2.5rem;
|
|
371
|
+
right: 2.5rem;
|
|
372
|
+
width: 3.5rem;
|
|
373
|
+
height: 3.5rem;
|
|
374
|
+
border-radius: 50%;
|
|
375
|
+
background-color: $primary-color;
|
|
376
|
+
color: #fff;
|
|
377
|
+
border: none;
|
|
378
|
+
font-size: 1.5rem;
|
|
379
|
+
display: flex;
|
|
380
|
+
align-items: center;
|
|
381
|
+
justify-content: center;
|
|
382
|
+
cursor: pointer;
|
|
383
|
+
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2);
|
|
384
|
+
transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
|
385
|
+
z-index: 1000;
|
|
386
|
+
|
|
387
|
+
&:hover {
|
|
388
|
+
transform: translateY(-4px) scale(1.05);
|
|
389
|
+
background-color: color.adjust($primary-color, $lightness: -5%);
|
|
390
|
+
box-shadow: 0 12px 20px rgba($primary-color, 0.3);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
&:active {
|
|
394
|
+
transform: translateY(-1px);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { useFooter } from '../composables/useFooter.js'
|
|
3
|
+
import { ref } from 'vue'
|
|
4
|
+
|
|
5
|
+
const props = defineProps({
|
|
6
|
+
config: {
|
|
7
|
+
type: Object,
|
|
8
|
+
default: () => ({})
|
|
9
|
+
}
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
const { brand, navigation, social, legal, version, copyright, newsletter } = useFooter(props.config)
|
|
13
|
+
const email = ref('')
|
|
14
|
+
const emit = defineEmits(['subscribe'])
|
|
15
|
+
|
|
16
|
+
const handleSubscribe = () => {
|
|
17
|
+
if (email.value) {
|
|
18
|
+
emit('subscribe', email.value)
|
|
19
|
+
email.value = ''
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
<template>
|
|
25
|
+
<footer class="tv-footer">
|
|
26
|
+
<div class="tv-footer__container">
|
|
27
|
+
<div class="tv-footer__left-column">
|
|
28
|
+
<slot name="brand" :brand="brand" :version="version">
|
|
29
|
+
<div v-if="brand" class="tv-footer__brand">
|
|
30
|
+
<a :href="brand.url || '/'" class="tv-footer__logo">
|
|
31
|
+
<img v-if="brand.logo" :src="brand.logo" :alt="brand.name" />
|
|
32
|
+
<span v-if="brand.name">{{ brand.name }}</span>
|
|
33
|
+
</a>
|
|
34
|
+
<span v-if="version" class="tv-footer__version">{{ version }}</span>
|
|
35
|
+
</div>
|
|
36
|
+
</slot>
|
|
37
|
+
|
|
38
|
+
<slot name="newsletter" :newsletter="newsletter">
|
|
39
|
+
<div v-if="newsletter" class="tv-footer__newsletter">
|
|
40
|
+
<h3 v-if="newsletter.title" class="tv-footer__newsletter-title">{{ newsletter.title }}</h3>
|
|
41
|
+
<p v-if="newsletter.description" class="tv-footer__newsletter-text">{{ newsletter.description }}</p>
|
|
42
|
+
<form class="tv-footer__newsletter-form" @submit.prevent="handleSubscribe">
|
|
43
|
+
<input
|
|
44
|
+
v-model="email"
|
|
45
|
+
type="email"
|
|
46
|
+
:placeholder="newsletter.placeholder || 'Enter your email'"
|
|
47
|
+
class="tv-footer__newsletter-input"
|
|
48
|
+
required
|
|
49
|
+
/>
|
|
50
|
+
<button type="submit" class="tv-footer__newsletter-button">
|
|
51
|
+
{{ newsletter.buttonText || 'Subscribe' }}
|
|
52
|
+
</button>
|
|
53
|
+
</form>
|
|
54
|
+
</div>
|
|
55
|
+
</slot>
|
|
56
|
+
</div>
|
|
57
|
+
|
|
58
|
+
<div class="tv-footer__nav-definitions">
|
|
59
|
+
<div v-for="(group, index) in navigation" :key="index" class="tv-footer__section">
|
|
60
|
+
<h3 v-if="group.title" class="tv-footer__section-title">{{ group.title }}</h3>
|
|
61
|
+
<ul class="tv-footer__links">
|
|
62
|
+
<li v-for="(link, i) in group.items" :key="i">
|
|
63
|
+
<a :href="link.url" class="tv-footer__link">
|
|
64
|
+
{{ link.label }}
|
|
65
|
+
</a>
|
|
66
|
+
</li>
|
|
67
|
+
</ul>
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
|
|
71
|
+
<div v-if="social && social.length" class="tv-footer__social-section">
|
|
72
|
+
<div class="tv-footer__social">
|
|
73
|
+
<a v-for="(item, index) in social" :key="index" :href="item.url" class="tv-footer__social-link" target="_blank" rel="noopener noreferer">
|
|
74
|
+
<img v-if="item.iconUrl" :src="item.iconUrl" :alt="item.label" class="tv-footer__social-icon-img" />
|
|
75
|
+
<i v-else-if="item.icon" :class="item.icon"></i>
|
|
76
|
+
<span v-else>{{ item.label }}</span>
|
|
77
|
+
</a>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
|
|
82
|
+
<slot name="bottom" :copyright="copyright" :legal="legal">
|
|
83
|
+
<div class="tv-footer__bottom">
|
|
84
|
+
<div v-if="copyright">{{ copyright }}</div>
|
|
85
|
+
<div v-if="legal && legal.length" class="tv-footer__legal">
|
|
86
|
+
<ul class="tv-footer__links" style="flex-direction: row; gap: 1.5rem;">
|
|
87
|
+
<li v-for="(link, index) in legal" :key="index">
|
|
88
|
+
<a :href="link.url" class="tv-footer__link">
|
|
89
|
+
{{ link.label }}
|
|
90
|
+
</a>
|
|
91
|
+
</li>
|
|
92
|
+
</ul>
|
|
93
|
+
</div>
|
|
94
|
+
</div>
|
|
95
|
+
</slot>
|
|
96
|
+
</footer>
|
|
97
|
+
</template>
|
|
98
|
+
|
|
99
|
+
<style scoped>
|
|
100
|
+
|
|
101
|
+
</style>
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { computed } from 'vue'
|
|
2
|
+
|
|
3
|
+
export function useFooter(config) {
|
|
4
|
+
const brand = computed(() => config?.brand || null)
|
|
5
|
+
|
|
6
|
+
const navigation = computed(() => {
|
|
7
|
+
if (!config?.navigation || !Array.isArray(config.navigation)) return []
|
|
8
|
+
return config.navigation
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
const social = computed(() => {
|
|
12
|
+
if (!config?.social || !Array.isArray(config.social)) return []
|
|
13
|
+
return config.social
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
const legal = computed(() => {
|
|
17
|
+
if (!config?.legal || !Array.isArray(config.legal)) return []
|
|
18
|
+
return config.legal
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
const version = computed(() => config?.version || '')
|
|
22
|
+
|
|
23
|
+
const copyright = computed(() => config?.copyright || '')
|
|
24
|
+
|
|
25
|
+
const newsletter = computed(() => {
|
|
26
|
+
return config?.newsletter || null
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
const currentYear = new Date().getFullYear()
|
|
30
|
+
|
|
31
|
+
const formattedCopyright = computed(() => {
|
|
32
|
+
const text = copyright.value
|
|
33
|
+
return text.replace('{year}', currentYear)
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
brand,
|
|
38
|
+
navigation,
|
|
39
|
+
social,
|
|
40
|
+
legal,
|
|
41
|
+
version,
|
|
42
|
+
copyright: formattedCopyright,
|
|
43
|
+
newsletter
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { defineAsyncComponent } from 'vue'
|
|
3
|
+
import { demos } from './utils/mocks.js'
|
|
4
|
+
import { TvDemo } from '@todovue/tv-demo'
|
|
5
|
+
|
|
6
|
+
const TvFooter = defineAsyncComponent(/* webpackChunkName: "tvFooter" */() => import('../components/TvFooter.vue'))
|
|
7
|
+
</script>
|
|
8
|
+
|
|
9
|
+
<template>
|
|
10
|
+
<TvDemo
|
|
11
|
+
hide-background
|
|
12
|
+
:component="TvFooter"
|
|
13
|
+
:variants="demos"
|
|
14
|
+
:manual-emits="['subscribe']"
|
|
15
|
+
component-name="TvFooter"
|
|
16
|
+
npm-install="@todovue/tv-footer"
|
|
17
|
+
source-link="https://github.com/TODOvue/tv-footer"
|
|
18
|
+
url-clone="https://github.com/TODOvue/tv-footer.git"
|
|
19
|
+
version="1.1.1"
|
|
20
|
+
/>
|
|
21
|
+
</template>
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<TvFooter :config="conf" />
|
|
3
|
+
</template>
|
|
4
|
+
|
|
5
|
+
<script setup>
|
|
6
|
+
import { TvFooter } from '@todovue/tv-footer'
|
|
7
|
+
import '@todovue/tv-footer/style.css'
|
|
8
|
+
import FacebookIcon from './utils/icons/facebook.png'
|
|
9
|
+
import GitHubWithIcon from "../icons/github-white.svg";
|
|
10
|
+
import TODOvue from "../icons/todovue.png";
|
|
11
|
+
|
|
12
|
+
const conf = {
|
|
13
|
+
brand: {
|
|
14
|
+
logo: 'https://res.cloudinary.com/denj4fg7f/image/upload/v1766199952/logo_ohpadg.png', // Optional
|
|
15
|
+
url: '/'
|
|
16
|
+
},
|
|
17
|
+
version: 'v2.4.0',
|
|
18
|
+
navigation: [
|
|
19
|
+
{
|
|
20
|
+
title: 'Product',
|
|
21
|
+
items: [
|
|
22
|
+
{ label: 'Features', url: '/#' },
|
|
23
|
+
{ label: 'Pricing', url: '/#' },
|
|
24
|
+
{ label: 'Showcase', url: '/#' }
|
|
25
|
+
]
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
title: 'Resources',
|
|
29
|
+
items: [
|
|
30
|
+
{ label: 'Documentation', url: '/#' },
|
|
31
|
+
{ label: 'API Reference', url: '/#' },
|
|
32
|
+
{ label: 'Community', url: '/#' }
|
|
33
|
+
]
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
title: 'Company',
|
|
37
|
+
items: [
|
|
38
|
+
{ label: 'About Us', url: '/#' },
|
|
39
|
+
{ label: 'Blog', url: '/#' },
|
|
40
|
+
{ label: 'Careers', url: '/#' }
|
|
41
|
+
]
|
|
42
|
+
}
|
|
43
|
+
],
|
|
44
|
+
social: [
|
|
45
|
+
{
|
|
46
|
+
label: 'GitHub',
|
|
47
|
+
url: 'https://github.com/TODOvue',
|
|
48
|
+
iconUrl: GitHubWithIcon // Icon library class (e.g. FontAwesome, UnoCSS) use /icon
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
label: 'Facebook',
|
|
52
|
+
url: 'https://facebook.com',
|
|
53
|
+
iconUrl: FacebookIcon
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
label: 'TODOvue',
|
|
57
|
+
url: 'https://todovue.blog',
|
|
58
|
+
iconUrl: TODOvue
|
|
59
|
+
}
|
|
60
|
+
],
|
|
61
|
+
legal: [
|
|
62
|
+
{ label: 'Privacy', url: '/#' },
|
|
63
|
+
{ label: 'Terms', url: '/#' },
|
|
64
|
+
{ label: 'Cookies', url: '/#' }
|
|
65
|
+
],
|
|
66
|
+
copyright: '© {year} TvFooter. All rights reserved.'
|
|
67
|
+
}
|
|
68
|
+
</script>
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<TvFooter :config="conf" @subscribe="handleSubscribe" />
|
|
3
|
+
</template>
|
|
4
|
+
|
|
5
|
+
<script setup>
|
|
6
|
+
import { TvFooter } from '@todovue/tv-footer'
|
|
7
|
+
import '@todovue/tv-footer/style.css'
|
|
8
|
+
import FacebookIcon from './utils/icons/facebook.png'
|
|
9
|
+
import GitHubWithIcon from "../icons/github-white.svg";
|
|
10
|
+
import TODOvue from "../icons/todovue.png";
|
|
11
|
+
|
|
12
|
+
const handleSubscribe = (email) => {
|
|
13
|
+
console.log('Subscribe event received:', email);
|
|
14
|
+
alert(`Subscribed with: ${email}`);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const conf = {
|
|
18
|
+
newsletter: {
|
|
19
|
+
title: 'Subscribe to our newsletter',
|
|
20
|
+
description: 'Get the latest news and updates right to your inbox.',
|
|
21
|
+
placeholder: 'Your email address',
|
|
22
|
+
buttonText: 'Subscribe'
|
|
23
|
+
},
|
|
24
|
+
brand: {
|
|
25
|
+
logo: 'https://res.cloudinary.com/denj4fg7f/image/upload/v1766199952/logo_ohpadg.png', // Optional
|
|
26
|
+
url: '/'
|
|
27
|
+
},
|
|
28
|
+
version: 'v2.4.0',
|
|
29
|
+
navigation: [
|
|
30
|
+
{
|
|
31
|
+
title: 'Product',
|
|
32
|
+
items: [
|
|
33
|
+
{ label: 'Features', url: '/#' },
|
|
34
|
+
{ label: 'Pricing', url: '/#' },
|
|
35
|
+
{ label: 'Showcase', url: '/#' }
|
|
36
|
+
]
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
title: 'Resources',
|
|
40
|
+
items: [
|
|
41
|
+
{ label: 'Documentation', url: '/#' },
|
|
42
|
+
{ label: 'API Reference', url: '/#' },
|
|
43
|
+
{ label: 'Community', url: '/#' }
|
|
44
|
+
]
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
title: 'Company',
|
|
48
|
+
items: [
|
|
49
|
+
{ label: 'About Us', url: '/#' },
|
|
50
|
+
{ label: 'Blog', url: '/#' },
|
|
51
|
+
{ label: 'Careers', url: '/#' }
|
|
52
|
+
]
|
|
53
|
+
}
|
|
54
|
+
],
|
|
55
|
+
social: [
|
|
56
|
+
{
|
|
57
|
+
label: 'GitHub',
|
|
58
|
+
url: 'https://github.com/TODOvue',
|
|
59
|
+
iconUrl: GitHubWithIcon // Icon library class (e.g. FontAwesome, UnoCSS) use /icon
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
label: 'Facebook',
|
|
63
|
+
url: 'https://facebook.com',
|
|
64
|
+
iconUrl: FacebookIcon
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
label: 'TODOvue',
|
|
68
|
+
url: 'https://todovue.blog',
|
|
69
|
+
iconUrl: TODOvue
|
|
70
|
+
}
|
|
71
|
+
],
|
|
72
|
+
legal: [
|
|
73
|
+
{ label: 'Privacy', url: '/#' },
|
|
74
|
+
{ label: 'Terms', url: '/#' },
|
|
75
|
+
{ label: 'Cookies', url: '/#' }
|
|
76
|
+
],
|
|
77
|
+
copyright: '© {year} TvFooter. All rights reserved.'
|
|
78
|
+
}
|
|
79
|
+
</script>
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg width="98" height="96" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z" fill="#fff"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg width="98" height="96" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z" fill="#24292f"/></svg>
|
|
Binary file
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import GitHubWithIcon from './icons/github-white.svg';
|
|
2
|
+
import FacebookIcon from './icons/facebook.png';
|
|
3
|
+
import TODOvue from './icons/todovue.png';
|
|
4
|
+
|
|
5
|
+
import Default from './demos/default.vue?raw';
|
|
6
|
+
import Newsletter from './demos/newsletter.vue?raw';
|
|
7
|
+
|
|
8
|
+
const config = {
|
|
9
|
+
brand: {
|
|
10
|
+
logo: 'https://res.cloudinary.com/denj4fg7f/image/upload/v1766199952/logo_ohpadg.png', // Optional
|
|
11
|
+
url: '/'
|
|
12
|
+
},
|
|
13
|
+
version: 'v2.4.0',
|
|
14
|
+
navigation: [
|
|
15
|
+
{
|
|
16
|
+
title: 'Product',
|
|
17
|
+
items: [
|
|
18
|
+
{ label: 'Features', url: '/#' },
|
|
19
|
+
{ label: 'Pricing', url: '/#' },
|
|
20
|
+
{ label: 'Showcase', url: '/#' }
|
|
21
|
+
]
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
title: 'Resources',
|
|
25
|
+
items: [
|
|
26
|
+
{ label: 'Documentation', url: '/#' },
|
|
27
|
+
{ label: 'API Reference', url: '/#' },
|
|
28
|
+
{ label: 'Community', url: '/#' }
|
|
29
|
+
]
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
title: 'Company',
|
|
33
|
+
items: [
|
|
34
|
+
{ label: 'About Us', url: '/#' },
|
|
35
|
+
{ label: 'Blog', url: '/#' },
|
|
36
|
+
{ label: 'Careers', url: '/#' }
|
|
37
|
+
]
|
|
38
|
+
}
|
|
39
|
+
],
|
|
40
|
+
social: [
|
|
41
|
+
{
|
|
42
|
+
label: 'GitHub',
|
|
43
|
+
url: 'https://github.com/TODOvue',
|
|
44
|
+
iconUrl: GitHubWithIcon // Icon library class (e.g. FontAwesome, UnoCSS)
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
label: 'Facebook',
|
|
48
|
+
url: 'https://facebook.com',
|
|
49
|
+
iconUrl: FacebookIcon
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
label: 'TODOvue',
|
|
53
|
+
url: 'https://todovue.blog',
|
|
54
|
+
iconUrl: TODOvue
|
|
55
|
+
}
|
|
56
|
+
],
|
|
57
|
+
legal: [
|
|
58
|
+
{ label: 'Privacy', url: '/#' },
|
|
59
|
+
{ label: 'Terms', url: '/#' },
|
|
60
|
+
{ label: 'Cookies', url: '/#' }
|
|
61
|
+
],
|
|
62
|
+
copyright: '© {year} TvFooter. All rights reserved.'
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const configEnhanced = {
|
|
66
|
+
...config,
|
|
67
|
+
newsletter: {
|
|
68
|
+
title: 'Subscribe to our newsletter',
|
|
69
|
+
description: 'Get the latest news and updates right to your inbox.',
|
|
70
|
+
placeholder: 'Your email address',
|
|
71
|
+
buttonText: 'Subscribe'
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export const demos = [
|
|
76
|
+
{
|
|
77
|
+
id: 1,
|
|
78
|
+
title: "TvFooter Default",
|
|
79
|
+
description: "Default TvFooter component",
|
|
80
|
+
propsData: {
|
|
81
|
+
config,
|
|
82
|
+
},
|
|
83
|
+
html: Default,
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
id: 2,
|
|
87
|
+
title: "TvFooter Enhanced",
|
|
88
|
+
description: "TvFooter with Newsletter",
|
|
89
|
+
propsData: {
|
|
90
|
+
config: configEnhanced,
|
|
91
|
+
},
|
|
92
|
+
html: Newsletter,
|
|
93
|
+
},
|
|
94
|
+
];
|
package/src/entry.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { App, Plugin } from 'vue'
|
|
2
|
+
import _TvFooter from './components/TvFooter.vue'
|
|
3
|
+
import './style.scss'
|
|
4
|
+
|
|
5
|
+
const TvFooter = _TvFooter as typeof _TvFooter & Plugin;
|
|
6
|
+
TvFooter.install = (app: App) => {
|
|
7
|
+
app.component('TvFooter', TvFooter)
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export { TvFooter }
|
|
11
|
+
|
|
12
|
+
export const TvFooterPlugin: Plugin = {
|
|
13
|
+
install: TvFooter.install
|
|
14
|
+
};
|
|
15
|
+
export default TvFooter;
|
|
16
|
+
|
|
17
|
+
declare module 'vue' {
|
|
18
|
+
export interface GlobalComponents {
|
|
19
|
+
TvFooter: typeof TvFooter;
|
|
20
|
+
}
|
|
21
|
+
}
|
package/src/main.js
ADDED
package/src/style.scss
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@use "./assets/scss/style";
|