How to change root css variables in Vue js so that it can be further used in naming tailwind classes? [solved] - vue.js

I have been trying my best to achieve an effect where upon changing the song in a music player, the image and the color scheme changes as well. This was possible when I made the music player using tailwindCSS and Vanilla Javascript. But it has been difficult for me to find a way to achieve the same using Vue.
While using Vanilla JS, I could write this in my JS and HTML file respectively:
function loadsong(music) {
song.innerText = music.song
artist.innerHTML = music.artist
audio.src = music.songUrl
document.documentElement.style.setProperty('--primary-color', music.prColor)
document.documentElement.style.setProperty('--secondary-color', music.secColor)
document.documentElement.style.setProperty('--img-link', music.imageSrc)
}
<style>
:root{
--primary-color: null;
--secondary-color: null;
--img-link: null;
}
</style>
<script>
tailwind.config = {
theme: {
colors: {
primary: 'var(--primary-color)',
secondary: 'var(--secondary-color)',
},
extend: {
backgroundImage: {
'image': 'var(--img-link)'
},
}
},
}
</script>
but when I try doing this in Vue JS:
export default {
name: 'app',
data() {
return {
current: {},
index: 0,
isPlaying: false,
player: new Audio(),
songData: [{ //an object for example
imageSrc: require('./assets/Images/Laufey.png'),
song: 'I Wish You Love',
songUrl: require('./assets/audio/Laufey - I Wish You Love.mp3'),
artist: 'Laufey',
prColor: '#46352E',
secColor: '#CCC6C4',
heart: true
}],
}
},
methods: {
next() {
this.index++
this.current = this.songData[this.index]
}
},
created() {
this.current = this.songData[this.index]
let root = document.documentElement.style
root.setProperty('--primary-color', this.current.prColor)
root.setProperty('--secondary-color', this.current.secColor)
root.setProperty('--img-link', `url(${this.current.imageSrc})`)
this.player.src = this.current.songUrl
console.log(this.current)
}
} <
/script>
#tailwind base;
#tailwind components;
#tailwind utilities;
#layer base {
:root {
--primary-color: null;
--secondary-color: null;
--img-link: null;
}
}
This does not work. I wanted that everytime I change the song, the script reads the new song object (which it does since it grabs other elements) grabs the colors I have defined in the object and pass it on to the root CSS variables. Can someone help me out with this?
Edit: When I used computed method to grab onto the root values and update them, it worked!

Related

VueJS Leaflet 'moveend' fires multiple times

Ask for help from the community. For two weeks I can not overcome the problem with repeated firing of 'mooveend' in the project. I have tried all the advice given here. Here's what I've read and researched already, but it didn't work for me.
This is one of the tips:
moveend event fired many times when page is load with Leaflet
<template>
<div id="map"></div>
</template>
<script>
export default {
name: "ObjectMapView",
props: ['coordinate'],
data: function () {
return {
map: null,
addressPoints: null,
markers: null,
}
},
mounted: function() {
this.initializedMap();
},
watch: {
coordinate: function (val) {
this.run();
}
},
methods: {
initializedMap: function () {
this.map = L.map('map').setView([52.5073390000,5.4742833000], 13);
L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors'
}).addTo(this.map);
this.markers = L.markerClusterGroup();
},
run: function () {
var map = this.map;
var markers = this.markers;
var getAllObjects = this.coordinate;
var getBoundsMarkers;
//Clearing Layers When Switching a Filter
markers.clearLayers();
this.addressPoints = getAllObjects.map(function (latlng){
return [latlng.latitude, latlng.longitude, latlng.zip, latlng.object_id, latlng.archived];
});
map.addLayer(markers);
//We give to the map only those coordinates that are in the zone of visibility of the map during the first
getBoundsMarkers = getAllObjects.filter((coord) => {
if(!coord.latitude && !coord.longitude){
return false;
}
return map.getBounds().contains(L.latLng(coord.latitude, coord.longitude));
});
/*
Responds to changing the boundaries of the map visibility zone and
transmits a list of coordinates that are in the visibility zone
*/
console.log('getAllObjects_1', getAllObjects);
map.on('moveend', function() {
console.log('moveend');
console.log('getAllObjects_2', getAllObjects);
getBoundsMarkers = getAllObjects.filter((coord) => {
if(!coord.latitude && !coord.longitude){
return false;
}
return map.getBounds().contains(L.latLng(coord.latitude, coord.longitude));
});
eventHub.$emit('sendMarkers', getBoundsMarkers);
});
// In the loop, we iterate over the coordinates and give them to the map
for (var i = 0; i < this.addressPoints.length; i++) {
var a = this.addressPoints[i];
var title = '' + a[2] + ''; //bubble
var marker = L.marker(new L.LatLng(a[0], a[1]), {
title: title
});
marker.bindPopup(title);
markers.addLayer(marker);
}
eventHub.$emit('sendMarkers', getBoundsMarkers);
}
}
}
</script>
<style scoped>
#map {
width: 97%;
height: 100%;
}
</style>
I figured it out myself.
The 'zoomend' and 'dragend' option didn't work for me. I searched a lot for a suitable option and realized that the "moveend" event fires several times because this event is created every time you move the map. Therefore it is necessary to stop this event. I got out of the situation in this way. Immediately after the map was initialized, I wrote:
map.off('moveend');
and for me it worked. Now it works fine. I will be very happy if this is useful to someone.

Handle paste event in vue2-editor

I'm using this text editor https://github.com/davidroyer/vue2-editor that is based on Quilljs
I want to handle the paste event so it pastes only the plain text without any format but seems in the documentation that paste is not a supported event by default.
Is there any way to add the paste event?
I've already tried using v-on:paste in the Editor and adding the Quill custom module Clipboard but haven't had any success.
As I didn't find a way of doing it with the library I did it with the DOM
onPaste() {
const x = document.getElementById("removePasteFormat");
console.log(x);
x.addEventListener("paste", (e) => {
e.stopPropagation();
e.preventDefault();
let text = e.clipboardData.getData("text/plain");
// access the clipboard using the api
if (document.queryCommandSupported("insertText")) {
document.execCommand("insertText", false, text);
} else {
document.execCommand("paste", false, text);
}
});
},
Added the id to the div containing the text editors like this:
<div id="removePasteFormat"> *<<Here goes the text editor component>>* </div>
And register the method on mounted()
mounted() {
this.onPaste();
},
I think it would be good to make a plugin.
I made it simple.
src/utils/vue2Plugin/clipboard.ts
import Delta from 'quill/node_modules/quill-delta';
import Clipboard from 'quill/modules/clipboard';
import { Quill } from 'vue2-editor';
export class CustomClipboardPlugin extends Clipboard {
public quill!: Quill;
public options: any = {};
constructor(quill: Quill) {
super(quill, {
matchers: [],
});
this.quill = quill;
this.quill.root.addEventListener('paste', this.onPaste.bind(this), true);
}
onPaste(event: ClipboardEvent) {
event.preventDefault();
event.stopPropagation();
const range = this.quill.getSelection(true);
if (range === null) return;
let _textHtml;
if (event.clipboardData?.getData('text/html')) {
_textHtml = event.clipboardData?.getData('text/html');
} else if (event.clipboardData?.getData('text/plain')) {
_textHtml = `<p>${event.clipboardData?.getData('text/plain')}</p>`;
}
if (_textHtml) {
const pastedDelta = this.quill.clipboard.convert(_textHtml);
const delta = new Delta()
.retain(range.index)
.delete(range.length)
.concat(pastedDelta);
this.quill.updateContents(delta, Quill.sources.USER);
this.quill.setSelection(delta.length() - range.length, 0, Quill.sources.SILENT);
}
}
}
vue file
<template>
...
<VueEditor
...
:custom-modules="customModulesForEditor"
...
/>
...
</template>
// script
import CustomClipboardPlugin fro 'src/utils/vue2Plugin/clipboard.ts';
...
data() {
return {
customModulesForEditor: [{ alias: 'clipboard', module: CustomClipboardPlugin }],
};
},
...
I was wondering the same and came up with the following solution.
1 - Use the :editorOptions option referenced here
<template>
VueEditor(:editorOptions="editorSettings")
</template>
2 - Fill with the module.clipboard module with the option described here
3 - You can then handle the paste with your personal function (applied after quill's matcher). I've written mine following this answer on github
<script>
data() {
return {
editorSettings: {
modules: {
clipboard: {
matchers: [[Node.ELEMENT_NODE, this.customQuillClipboardMatcher]]
}
}
}
}
},
methods: {
customQuillClipboardMatcher(node, delta) {
delta.ops = delta.ops.map((op) => {
return {
insert: op.insert
}
})
return delta
},
}
</script>

How to remap a Durandal viewmodel

This is my main.js file in a Durandal project.
What I'm trying to do is set things up so that the name 'upload-item' resolves to either 'upload-item' or 'upload-item-prehtml5' depending on whether File is defined.
requirejs.config({
paths: {
'text': '../lib/require/text',
'durandal': '../lib/durandal/js',
'plugins': '../lib/durandal/js/plugins',
'transitions': '../lib/durandal/js/transitions',
'knockout': '../lib/knockout/knockout-2.3.0',
'bootstrap': '../lib/bootstrap/js/bootstrap',
'jquery': '../lib/jquery/jquery-1.9.1.min',
'jquery-ui': '../lib/jquery-ui/jquery-ui-1.10.4.custom/js/jquery-ui-1.10.4.custom.min',
'moment': '../lib/moment/moment',
'knockout-jqueryui': '../lib/knockout/knockout-jqueryui.min',
'file-size-formatting': '../lib/wone/file-size-formatting'
},
shim: {
'bootstrap': {
deps: ['jquery'],
exports: 'jQuery'
}
}
});
define(['durandal/system', 'durandal/app', 'durandal/viewLocator'], function (system, app, viewLocator) {
//>>excludeStart("build", true);
system.debug(true);
//>>excludeEnd("build");
var filetype = typeof(File);
if (filetype == 'undefined') {
//apply pre-html5 fixups
require.config({
map: {
'*': {
'upload-item': 'upload-item-prehtml5'
}
}
});
}
app.title = 'Jumbo File Transfer';
//specify which plugins to install and their configuration
app.configurePlugins({
router: true,
dialog: true,
widget: {
kinds: ['expander']
}
});
app.start().then(function () {
//Replace 'viewmodels' in the moduleId with 'views' to locate the view.
//Look for partial views in a 'views' folder in the root.
viewLocator.useConvention();
//Show the app by setting the root view model for our application.
app.setRoot('shell');
});
});
Testing on IE8 shows that the call to require.config occurs and the mapping is added, but it doesn't seem to have the effect I expected: upload-item.js and upload-item.html are loaded when I expected upload-item-prehtml5.js and upload-item-prehtml5.html to be loaded.
If this is the wrong way to go about this, then what is the right way to perform this kind of conditional resolution?
It's not quite what I originally wanted, but I found you can do this:
var prehtml5 = (typeof (File) == 'undefined');
requirejs.config({
paths: {
...
'upload-item': prehtml5 ? 'upload-item-prehtml5' : 'upload-item'
},
shim: {
'bootstrap': {
deps: ['jquery'],
exports: 'jQuery'
}
}
});
Path remapping seems to extend into the file name. Normally you wouldn't list siblings of main.js but you can and if you do then you can remap them, including the file name.

i18next - All languages in one .json file

How can I make i18next load all languages from just one file?
I managed to do it by putting each language in a seperate file (translation-en.json, translation-no.json, etc), and also managed to input languages with the resStore option, but putting it all in a seperate .json file is really not documented anywhere (I've searched for 4 hours+ now)
My js code:
i18n.init({
debug: true,
lng: 'en',
resGetPath: 'translation.json'
},
function(t) {
console.log(t('test'));
});
My translation.json file:
{
en: {
translation: {
test: "some string"
}
},
no: {
translation: {
test: "litt tekst"
}
}
}
Ok, so I managed to "hack" it byt putting an object into a seperate .js file, include it in a script tag and loading it using resStore, but that just can't be the best way to use this lib.
Assume that your translation.json has loaded and assigned to a variable named resStore:
var resStore = {
en: {
translation: {
test: "some string"
}
},
no: {
translation: {
test: "litt tekst"
}
}
};
Next, you can override default ajax loading functionality with your customLoad function. An example might look like this:
var options = {
lng: 'en',
load: 'current',
lowerCaseLng: true,
fallbackLng: false,
resGetPath: 'i18n/__lng__/__ns__.json',
customLoad: function(lng, ns, options, loadComplete) {
var data = resStore[lng][ns];
loadComplete(null, data); // or loadComplete('some error'); if failed
},
ns: {
namespaces: ['translation'],
defaultNs: 'translation'
}
};
i18n.init(options, function(t) {
t('test'); // will get "some string"
});
new update on Mar 20, 2015
You can simply pass your resource store with the resStore option:
i18n.init({ resStore: resources });

Ext.ux.Image : Cannot read property 'dom' of undefined

I need a real <img> HTML tag in my view Sencha.
I've retrieved this code from the official doc :
Ext.define('Ext.ux.Image', {
extend: 'Ext.Component', // subclass Ext.Component
alias: 'widget.managedimage', // this component will have an xtype of 'managedimage'
autoEl: {
tag: 'img',
src: Ext.BLANK_IMAGE_URL,
cls: 'my-managed-image'
},
// Add custom processing to the onRender phase.
// Add a ‘load’ listener to the element.
onRender: function() {
this.autoEl = Ext.apply({}, this.initialConfig, this.autoEl);
this.callParent(arguments);
this.el.on('load', this.onLoad, this);
},
onLoad: function() {
this.fireEvent('load', this);
},
setSrc: function(src) {
if (this.rendered) {
this.el.dom.src = src;
} else {
this.src = src;
}
},
getSrc: function(src) {
return this.el.dom.src || this.src;
}
});
When i try to do setSrc, I get this error : Cannot read property 'dom' of undefined
Your code is from Ext.Js 4.x docs. You should use sencha touch 2 docs.
Please compare:
http://docs.sencha.com/ext-js/4-1/#!/api/Ext.Component
and
http://docs.sencha.com/touch/2-0/#!/api/Ext.Component
They are different.
As i understand you need real < img > tag in your view. If you use Ext.Img it will create a div container with background-image.
I know two ways:
set up tpl and data property.
Ext.create('Ext.Component', {
config: {
tpl: '',
data: {
url: 'http://example.com/pics/1.png',
imgClass: 'my-class'
}
}
});
set html config.
Ext.create('Ext.Component', {
config: {
html: ' <img class="my-class" src="http://example.com/pics/1.png">'
}
});