HI team,
First of all ; thank you for the great work. Metronic is the best.
Without surprise, I need more component / librairies on Vue templates, as Tiny sliders for example.
scss is here, but no js or ts.
Can you help me adding it ?
Best regards
Thanks a lot Lauris, it's working now.
You forgot : data-tns="true"
And I replaced tns-initiazlied
to tns-initialized
in scss source .
Best regards,
Wilhem
Hi,
Glad to hear that. All the best with your project!
Regards,
Lauris Stepanovs,
Keenthemes Support Team
Still not working, here the component based on html =
<template>
<!--begin::Team Section-->
<div class="py-10 py-lg-20">
<!--begin::Container-->
<div class="container">
<!--begin::Heading-->
<div class="text-center mb-12">
<!--begin::Title-->
<h3
class="fs-2hx text-dark mb-5"
data-kt-scroll-offset="{default: 100, lg: 150}">
Our Great Team
<!--end::Title-->
<!--begin::Sub-title-->
<div class="fs-5 text-muted fw-bold">
It’s no doubt that when a development takes longer to complete,
additional costs to <br />integrate and test each extra feature creeps
up and haunts most of us.
</div>
<!--end::Sub-title=-->
</div>
<!--end::Heading-->
<!--begin::Slider-->
<div class="tns tns-default" ref="sliderRef">
<!--begin::Wrapper-->
<div
data-tns="true"
data-tns-loop="true"
data-tns-swipe-angle="false"
data-tns-speed="2000"
data-tns-autoplay="true"
data-tns-autoplay-timeout="18000"
data-tns-controls="true"
data-tns-nav="false"
data-tns-items="1"
data-tns-center="false"
data-tns-dots="false"
data-tns-prev-button="#kt_team_slider_prev"
data-tns-next-button="#kt_team_slider_next"
data-tns-responsive="{1200: {items: 3}, 992: {items: 2}}">
<!--begin::Item-->
<div class="text-center">
<!--begin::Photo-->
<div
class="octagon mx-auto mb-5 d-flex w-200px h-200px bgi-no-repeat bgi-size-contain bgi-position-center"
></div>
<!--end::Photo-->
<!--begin::Person-->
<div class="mb-0">
<!--begin::Name-->
<a href="#" class="text-dark fw-bold text-hover-primary fs-3"
>Paul Miles</a
>
<!--end::Name-->
<!--begin::Position-->
<div class="text-muted fs-6 fw-semibold mt-1">
Development Lead
</div>
<!--begin::Position-->
</div>
<!--end::Person-->
</div>
<!--end::Item-->
<!--begin::Item-->
<div class="text-center">
<!--begin::Photo-->
<div
class="octagon mx-auto mb-5 d-flex w-200px h-200px bgi-no-repeat bgi-size-contain bgi-position-center"
></div>
<!--end::Photo-->
<!--begin::Person-->
<div class="mb-0">
<!--begin::Name-->
<a href="#" class="text-dark fw-bold text-hover-primary fs-3"
>Melisa Marcus</a
>
<!--end::Name-->
<!--begin::Position-->
<div class="text-muted fs-6 fw-semibold mt-1">
Creative Director
</div>
<!--begin::Position-->
</div>
<!--end::Person-->
</div>
<!--end::Item-->
<!--begin::Item-->
<div class="text-center">
<!--begin::Photo-->
<div
class="octagon mx-auto mb-5 d-flex w-200px h-200px bgi-no-repeat bgi-size-contain bgi-position-center"
></div>
<!--end::Photo-->
<!--begin::Person-->
<div class="mb-0">
<!--begin::Name-->
<a href="#" class="text-dark fw-bold text-hover-primary fs-3"
>David Nilson</a
>
<!--end::Name-->
<!--begin::Position-->
<div class="text-muted fs-6 fw-semibold mt-1">Python Expert</div>
<!--begin::Position-->
</div>
<!--end::Person-->
</div>
<!--end::Item-->
<!--begin::Item-->
<div class="text-center">
<!--begin::Photo-->
<div
class="octagon mx-auto mb-5 d-flex w-200px h-200px bgi-no-repeat bgi-size-contain bgi-position-center"
></div>
<!--end::Photo-->
<!--begin::Person-->
<div class="mb-0">
<!--begin::Name-->
<a href="#" class="text-dark fw-bold text-hover-primary fs-3"
>Anne Clarc</a
>
<!--end::Name-->
<!--begin::Position-->
<div class="text-muted fs-6 fw-semibold mt-1">
Project Manager
</div>
<!--begin::Position-->
</div>
<!--end::Person-->
</div>
<!--end::Item-->
<!--begin::Item-->
<div class="text-center">
<!--begin::Photo-->
<div
class="octagon mx-auto mb-5 d-flex w-200px h-200px bgi-no-repeat bgi-size-contain bgi-position-center"
></div>
<!--end::Photo-->
<!--begin::Person-->
<div class="mb-0">
<!--begin::Name-->
<a href="#" class="text-dark fw-bold text-hover-primary fs-3"
>Ricky Hunt</a
>
<!--end::Name-->
<!--begin::Position-->
<div class="text-muted fs-6 fw-semibold mt-1">Art Director</div>
<!--begin::Position-->
</div>
<!--end::Person-->
</div>
<!--end::Item-->
<!--begin::Item-->
<div class="text-center">
<!--begin::Photo-->
<div
class="octagon mx-auto mb-5 d-flex w-200px h-200px bgi-no-repeat bgi-size-contain bgi-position-center"
></div>
<!--end::Photo-->
<!--begin::Person-->
<div class="mb-0">
<!--begin::Name-->
<a href="#" class="text-dark fw-bold text-hover-primary fs-3"
>Alice Wayde</a
>
<!--end::Name-->
<!--begin::Position-->
<div class="text-muted fs-6 fw-semibold mt-1">
Marketing Manager
</div>
<!--begin::Position-->
</div>
<!--end::Person-->
</div>
<!--end::Item-->
<!--begin::Item-->
<div class="text-center">
<!--begin::Photo-->
<div
class="octagon mx-auto mb-5 d-flex w-200px h-200px bgi-no-repeat bgi-size-contain bgi-position-center"
></div>
<!--end::Photo-->
<!--begin::Person-->
<div class="mb-0">
<!--begin::Name-->
<a href="#" class="text-dark fw-bold text-hover-primary fs-3"
>Carles Puyol</a
>
<!--end::Name-->
<!--begin::Position-->
<div class="text-muted fs-6 fw-semibold mt-1">QA Managers</div>
<!--begin::Position-->
</div>
<!--end::Person-->
</div>
<!--end::Item-->
</div>
<!--end::Wrapper-->
<!--begin::Button-->
<button
class="btn btn-icon btn-active-color-primary"
>
<!--begin::Svg Icon | path: icons/duotune/arrows/arr074.svg-->
<span class="svg-icon svg-icon-3x">
<inline-svg
:src="getAssetPath("/media/icons/duotune/arrows/arr074.svg")" />
</span>
<!--end::Svg Icon-->
</button>
<!--end::Button-->
<!--begin::Button-->
<button
class="btn btn-icon btn-active-color-primary"
>
<!--begin::Svg Icon | path: icons/duotune/arrows/arr071.svg-->
<span class="svg-icon svg-icon-3x">
<inline-svg
:src="getAssetPath("/media/icons/duotune/arrows/arr071.svg")" />
</span>
<!--end::Svg Icon-->
</button>
<!--end::Button-->
</div>
<!--end::Slider-->
</div>
<!--end::Container-->
</div>
<!--end::Team Section-->
</template>
<script lang="ts">
import { defineComponent, onMounted, ref} from "vue";
import { getAssetPath } from "@/core/helpers/assets";
import { tns } from "tiny-slider";
export default defineComponent({
name: "team",
components: {},
props: {
variable: {
type: [Object, String, Number, Boolean],
default: false,
},
//...
},
setup(props) {
// Expose it to the template, if required
const sliderRef = ref<HTMLElement | null>(null);
var createTinySliders = function () {
if (typeof tns === "undefined") {
return;
}
if (typeof sliderRef.value === "undefined") {
return;
}
// Init Slider
var initSlider = function (el) {
if (!el) {
return;
}
const tnsOptions = {};
// Convert string boolean
const checkBool = function (val) {
if (val === "true") {
return true;
}
if (val === "false") {
return false;
}
return val;
};
// get extra options via data attributes
el.getAttributeNames().forEach(function (attrName) {
// more options; https://github.com/ganlanyuan/tiny-slider#options
if ((/^data-tns-.*/g).test(attrName)) {
let optionName = attrName.replace("data-tns-", "").toLowerCase().replace(/(?:[\s-])\w/g, function (match) {
return match.replace("-", "").toUpperCase();
});
if (attrName === "data-tns-responsive") {
// fix string with a valid json
const jsonStr = el.getAttribute(attrName).replace(/(\w+<img alt="happy" src="https://devs.keenthemes.com/assets/media/smiles/happy.png">|(\w+ <img alt="happy" src="https://devs.keenthemes.com/assets/media/smiles/happy.png">/g, function (matched) {
return """ + matched.substring(0, matched.length - 1) + "":";
});
try {
// convert json string to object
tnsOptions[optionName] = JSON.parse(jsonStr);
}
catch (e) {
}
}
else {
tnsOptions[optionName] = checkBool(el.getAttribute(attrName));
}
}
});
const opt = Object.assign({}, {
container: el,
slideBy: "page",
autoplay: true,
autoplayButtonOutput: false,
}, tnsOptions);
if (el.closest(".tns")) {
sliderRef.value?.classList.add("tns-initialized");
}
return tns(opt);
}
// Sliders
const elements = Array.prototype.slice.call(document.querySelectorAll("[data-tns="true"]"), 0);
if (!elements && elements.length === 0) {
return;
}
elements.forEach(function (el) {
if (el.getAttribute("data-kt-initialized") === "1") {
return;
}
initSlider(el);
el.setAttribute("data-kt-initialized", "1");
});
}
onMounted(() => {
createTinySliders();
});
return { getAssetPath, sliderRef };
},
});
</script>
<style lang="scss">
@import "tiny-slider/dist/tiny-slider.css";
</style>
Hi,
The example above should work, here is the gist with a full component code.
https://gist.github.com/laurisstepanovs/dca0824c8cf2889ebb959762ed39a358
Could you please try it in your version?
Regards,
Lauris Stepanovs,
Keenthemes Support Team
I'm sorry Lauris, but I can't make it work,
can you describe step 4 ?
"Then you can reuse the slider init function from our HTML version."
Where did I put this code ?
Best, Wilhem
Hi Wilhem,
You can put those functions inside your components setup function.
import {
...
onMounted
} from "vue";
...
setup(){
var createTinySliders = function () {
...
}
onMounted(() => {
createTinySliders();
});
}
...
Hi,
Sorry for the late reply.
To add tiny-slider to our Vue version you can follow instructions below:
npm install tiny-slider
.<style lang="scss">
@import "tiny-slider/dist/tiny-slider.css";
</style>
<div class="py-5">
<div class="rounded border p-5 p-lg-15">
<div class="tns tns-default" >
<!--begin::Slider-->
<div
data-tns="true"
data-tns-loop="true"
data-tns-swipe-angle="false"
data-tns-speed="2000"
data-tns-autoplay="true"
data-tns-autoplay-timeout="18000"
data-tns-controls="true"
data-tns-nav="false"
data-tns-items="3"
data-tns-center="false"
data-tns-dots="false"
data-tns-prev-button="#kt_team_slider_prev1"
data-tns-next-button="#kt_team_slider_next1"
>
<!--begin::Item-->
<div class="text-center px-5 py-5">
<img
:src="getAssetPath("/media/stock/600x400/img-1.jpg")"
class="card-rounded mw-100"
alt=""
/>
</div>
<!--end::Item-->
<!--begin::Item-->
<div class="text-center px-5 py-5">
<img
:src="getAssetPath("/media/stock/600x400/img-1.jpg")"
class="card-rounded mw-100"
alt=""
/>
</div>
<!--end::Item-->
<!--begin::Item-->
<div class="text-center px-5 py-5">
<img
:src="getAssetPath("/media/stock/600x400/img-1.jpg")"
class="card-rounded mw-100"
alt=""
/>
</div>
<!--end::Item-->
<!--begin::Item-->
<div class="text-center px-5 py-5">
<img
:src="getAssetPath("/media/stock/600x400/img-1.jpg")"
class="card-rounded mw-100"
alt=""
/>
</div>
<!--end::Item-->
<!--begin::Item-->
<div class="text-center px-5 py-5">
<img
:src="getAssetPath("/media/stock/600x400/img-1.jpg")"
class="card-rounded mw-100"
alt=""
/>
</div>
<!--end::Item-->
<!--begin::Item-->
<div class="text-center px-5 py-5">
<img
:src="getAssetPath("/media/stock/600x400/img-1.jpg")"
class="card-rounded mw-100"
alt=""
/>
</div>
<!--end::Item-->
</div>
<!--end::Slider-->
<!--begin::Slider button-->
<button
class="btn btn-icon btn-active-color-primary"
>
<span class="svg-icon svg-icon-3x">
<inline-svg
:src="getAssetPath("media/icons/duotune/arrows/arr074.svg")"
/>
</span>
</button>
<!--end::Slider button-->
<!--begin::Slider button-->
<button
class="btn btn-icon btn-active-color-primary"
>
<span class="svg-icon svg-icon-3x">
<inline-svg
:src="getAssetPath("media/icons/duotune/arrows/arr071.svg")"
/>
</span>
</button>
<!--end::Slider button-->
</div>
</div>
</div>
var createTinySliders = function () {
if (typeof tns === "undefined") {
return;
}
// Init Slider
var initSlider = function (el) {
if (!el) {
return;
}
const tnsOptions = {};
// Convert string boolean
const checkBool = function (val) {
if (val === "true") {
return true;
}
if (val === "false") {
return false;
}
return val;
};
// get extra options via data attributes
el.getAttributeNames().forEach(function (attrName) {
// more options; https://github.com/ganlanyuan/tiny-slider#options
if (/^data-tns-.*/g.test(attrName)) {
let optionName = attrName
.replace("data-tns-", "")
.toLowerCase()
.replace(/(?:[\s-])\w/g, function (match) {
return match.replace("-", "").toUpperCase();
});
if (attrName === "data-tns-responsive") {
// fix string with a valid json
const jsonStr = el
.getAttribute(attrName)
.replace(/(\w+<img alt="happy" src="https://devs.keenthemes.com/assets/media/smiles/happy.png">|(\w+ <img alt="happy" src="https://devs.keenthemes.com/assets/media/smiles/happy.png">/g, function (matched) {
return """ + matched.substring(0, matched.length - 1) + "":";
});
try {
// convert json string to object
tnsOptions[optionName] = JSON.parse(jsonStr);
} catch (e) {}
} else {
tnsOptions[optionName] = checkBool(el.getAttribute(attrName));
}
}
});
const opt = Object.assign(
{},
{
container: el,
autoplay: true,
autoplayButtonOutput: false,
},
tnsOptions
);
if (el.closest(".tns")) {
el.closest(".tns").classList.add("tns-initiazlied");
}
return tns(opt);
};
// Sliders
const elements = Array.prototype.slice.call(
document.querySelectorAll("[data-tns="true"]"),
0
);
if (!elements) {
return;
}
elements.forEach(function (el) {
if (el.getAttribute("data-kt-initialized") === "1") {
return;
}
initSlider(el);
el.setAttribute("data-kt-initialized", "1");
});
};
onMounted(() => {
createTinySliders();
});