FullCalendar Add Icon with myID - fullcalendar

in the resources I would like to add an icon with an ID inside that I can pass on.
I tried with resourceLabelContent but it doesn't work for me
resourceGroupField: 'mygroup',
resourceColumns: [
{
labelText: 'Unità',
field: 'title',
},
{
labelText: 'Telefono',
field: 'Tel',
},
],
resources: {
url: 'fetch-resource.php',
method: 'POST',
},
resourceLabelContent: function (arg)
{
return { arg.resource.title + "'<i class="fas fa-users">'+arg.resource.myid+'</i>'"};
},

I solved it in part .... I modified the JS schduler so, in this way it puts the icons in the resources
ResourceRow.prototype.renderGutterHtml = function() {
var html, i, j, ref;
html = '';
for (i = j = 0, ref = this.depth; j < ref; i = j += 1) {
html += '<a href="#" class="far fa-envelope m-1 text-info sendMail"/></a>';
}
html += '<span class="fc-expander-space">' + '<a href="#" class="fas fa-chart-bar m-1 text-danger graphStat" color=red></a>' + '</span>';
return html;
};
then with "resourceRender" I go to assign the data-id, but then the click on the icon doesn't work
resourceRender: function(resource, cellEls) {
$('.graphStat').attr('data-id',resource.id);
$('.graphStat').on('click', function(e) {
alert($(this).data('id'));
});
},
what am I doing wrong

Related

DevExpress Web Dashboard in PiranhaCMS

I am currently working on a .NET Core application based on a CMS Framework named PiranhaCMS. This framework allows the definition of configurable "Blocks", basically widgets, that can be added by the users on their pages. The configuration page of the blocks is realized as a Vue.js component, and code is then compiled via gulp in a standard JS format (from the .vue file to the Vue.component(...) syntax) for the Piranha framework to read and render. The author of Piranha confirmed that this is the only way to define new blocks.
In one of our custom blocks, we are trying to implement a DevExpress Web Dashboard. I have tried following the steps outlined at https://docs.devexpress.com/Dashboard/401150/web-dashboard/dashboard-component-for-vue, but to no avail, since the compiler throws an Exception stating that the top-level declaration should be an export default { ... }, and not an import statement.
I came up with a workaround in which I dynamically load the required scripts and styles on the created() method of the component, and then define the dashboard in the same way I would in a classic javascript case (https://docs.devexpress.com/Dashboard/119158/web-dashboard/dashboard-control-for-javascript-applications-jquery-knockout-etc/add-web-dashboard-to-a-javascript-application);;) however, I am sure there is a more elegant solution to this problem.
Below is the code relevant to the problem. Here is the custom block itools-dashboard.vue:
<template>
<div class="form-group block-body">
<div :id="'dashboard-designer-' + uid" class="dashboard-designer">
<div :id="'dashboard_' + uid" style="height: 100%;">
</div>
</div>
<div class="row">
<div class="col-sm-6" style="padding:10px; margin-top: 0px;vertical-align: top;">
<fieldset>
<legend>Dashboard</legend>
<div class="form-group">
<label>Dashboard name</label>
<select class="form-control small" :id="'dashboard-names-' + uid" v-model="model.dashboardName.value">
<option v-for="dash in dashboardNames">{{ dash }}</option>
</select>
</div>
<div class="form-group">
<label>Update time</label>
<input class="form-control small" type="number" v-model="model.updateTime.value">
</div>
<div class="form-group">
<label>Width</label>
<input class="form-control small" type="text" v-model="model.width.value">
</div>
<div class="form-group">
<label>Height</label>
<input class="form-control small" type="text" v-model="model.height.value">
</div>
</fieldset>
</div>
<div class="col-sm-6" style="padding:10px; margin-top: 0px; background-color: #fcfcfc; border:1px dotted lightgray; vertical-align: top;">
<itools-base :model="model"></itools-base>
</div>
</div>
</div>
</template>
<script>
export default {
props: ["uid", "toolbar", "model"],
data: function () {
return {
dashboardNames: [],
dahsboardConfig: null,
updateModes: ["period", "realtime"],
basePath: "../../../../assets/",
// define all the css and js files paths
cssResources: [
"devextreme/dist/css/light.css",
"#devexpress/analytics-core/dist/css/dx-analytics.common.css",
"#devexpress/analytics-core/dist/css/dx-analytics.light.css",
"#devexpress/analytics-core/dist/css/dx-querybuilder.css",
"devexpress-dashboard/dist/css/dx-dashboard.light.min.css"
],
jsResources: [
"js/jquery/jquery-3.3.1.min.js",
"jquery-ui-dist/jquery-ui.js",
"knockout/build/output/knockout-latest.js",
"ace-builds/src-min-noconflict/ace.js",
"ace-builds/src-min-noconflict/ext-language_tools.js",
"ace-builds/src-min-noconflict/theme-dreamweaver.js",
"ace-builds/src-min-noconflict/theme-ambiance.js",
"devextreme/dist/js/dx.all.js",
"devextreme/dist/js/dx.aspnet.mvc.js",
"devextreme-aspnet-data/js/dx.aspnet.data.js",
"#devexpress/analytics-core/dist/js/dx-analytics-core.min.js",
"#devexpress/analytics-core/dist/js/dx-querybuilder.min.js",
"devexpress-dashboard/dist/js/dx-dashboard.min.js"
]
}
},
created: function () {
// dynamically add the required css
this.cssResources.forEach(x => {
let link = document.createElement("link");
link.setAttribute("href", this.basePath + x);
link.setAttribute("rel", "stylesheet");
document.head.appendChild(link);
});
// dynamically add the js files.
// It needs to be a synchronous ajax call so that the exports are visible in the code
// (eg the new DevExpress call)
this.jsResources.forEach(x => {
$.ajax({
async: false,
url: this.basePath + x,
dataType: "script"
})
});
this.model.width.value = this.model.width.value || "100%";
this.model.height.value = this.model.height.value || "300";
this.model.updateTime.value = this.model.updateTime.value || 5000;
},
mounted: function () {
var h = document.getElementById("dashboard-designer-" + this.uid).clientHeight;
DevExpress.Dashboard.ResourceManager.embedBundledResources();
var dashboardControl = new DevExpress.Dashboard.DashboardControl(document.getElementById("dashboard_" + this.uid), {
endpoint: "/api/dashboard",
workingMode: "Designer",
width: "100%",
height: "100%",
initialDashboardId: this.model.dashboardName.value,
});
dashboardControl.render();
},
beforeCreate: function () {
fetch("/api/Dashboards/GetDashboardNames")
.then(response => response.json())
.then(data => {
this.dashboardNames = data;
});
},
}
</script>
which is then compiled via gulp task to
Vue.component("itools-dashboard", {
props: ["uid", "toolbar", "model"],
data: function () {
return {
dashboardNames: [],
dahsboardConfig: null,
updateModes: ["period", "realtime"],
basePath: "../../../../assets/",
cssResources: ["devextreme/dist/css/light.css", "#devexpress/analytics-core/dist/css/dx-analytics.common.css", "#devexpress/analytics-core/dist/css/dx-analytics.light.css", "#devexpress/analytics-core/dist/css/dx-querybuilder.css", "devexpress-dashboard/dist/css/dx-dashboard.light.min.css"],
jsResources: ["js/jquery/jquery-3.3.1.min.js", "jquery-ui-dist/jquery-ui.js", "knockout/build/output/knockout-latest.js", "ace-builds/src-min-noconflict/ace.js", "ace-builds/src-min-noconflict/ext-language_tools.js", "ace-builds/src-min-noconflict/theme-dreamweaver.js", "ace-builds/src-min-noconflict/theme-ambiance.js", "devextreme/dist/js/dx.all.js", "devextreme/dist/js/dx.aspnet.mvc.js", "devextreme-aspnet-data/js/dx.aspnet.data.js", "#devexpress/analytics-core/dist/js/dx-analytics-core.min.js", "#devexpress/analytics-core/dist/js/dx-querybuilder.min.js", "devexpress-dashboard/dist/js/dx-dashboard.min.js"]
};
},
created: function () {
this.cssResources.forEach(x => {
let link = document.createElement("link");
link.setAttribute("href", this.basePath + x);
link.setAttribute("rel", "stylesheet");
document.head.appendChild(link);
});
this.jsResources.forEach(x => {
$.ajax({
async: false,
url: this.basePath + x,
dataType: "script"
});
});
this.model.width.value = this.model.width.value || "100%";
this.model.height.value = this.model.height.value || "300";
this.model.updateTime.value = this.model.updateTime.value || 5000;
},
mounted: function () {
DevExpress.Dashboard.ResourceManager.embedBundledResources();
var dashboardControl = new DevExpress.Dashboard.DashboardControl(document.getElementById("dashboard_" + this.uid), {
endpoint: "/api/dashboard",
workingMode: "Designer",
width: "100%",
height: "100%",
initialDashboardId: this.model.dashboardName.value
});
dashboardControl.render();
},
beforeCreate: function () {
fetch("/api/Dashboards/GetDashboardNames").then(response => response.json()).then(data => {
this.dashboardNames = data;
});
},
template: "\n<div class=\"form-group block-body\">\n <div :id=\"'dashboard-designer-' + uid\" class=\"dashboard-designer\">\n <div :id=\"'dashboard_' + uid\" style=\"height: 100%;\">\n </div>\n </div>\n <div class=\"row\">\n <div class=\"col-sm-6\" style=\"padding:10px; margin-top: 0px;vertical-align: top;\">\n <fieldset>\n <legend>Dashboard</legend>\n <div class=\"form-group\">\n <label>Dashboard name</label>\n <select class=\"form-control small\" :id=\"'dashboard-names-' + uid\" v-model=\"model.dashboardName.value\">\n <option v-for=\"dash in dashboardNames\">{{ dash }}</option>\n </select>\n </div>\n <div class=\"form-group\">\n <label>Update time</label>\n <input class=\"form-control small\" type=\"number\" v-model=\"model.updateTime.value\">\n </div>\n <div class=\"form-group\">\n <label>Width</label>\n <input class=\"form-control small\" type=\"text\" v-model=\"model.width.value\">\n </div>\n <div class=\"form-group\">\n <label>Height</label>\n <input class=\"form-control small\" type=\"text\" v-model=\"model.height.value\">\n </div>\n </fieldset>\n </div>\n <div class=\"col-sm-6\" style=\"padding:10px; margin-top: 0px; background-color: #fcfcfc; border:1px dotted lightgray; vertical-align: top;\">\n <itools-base :model=\"model\"></itools-base>\n </div>\n </div>\n</div>\n"
});
The gulp task responsible for the compilation, defined by Piranha, is:
var gulp = require('gulp'),
sass = require('gulp-sass'),
cssmin = require("gulp-cssmin"),
uglifyes = require('uglify-es'),
composer = require('gulp-uglify/composer'),
uglify = composer(uglifyes, console),
rename = require("gulp-rename"),
concat = require("gulp-concat");
var path = require('path'),
vueCompiler = require('vue-template-compiler'),
babel = require("#babel/core"),
babelTemplate = require("#babel/template").default,
codeFrameColumns = require('#babel/code-frame').codeFrameColumns,
babelTypes = require("#babel/types"),
through2 = require('through2');
function vueCompile() {
return through2.obj(function (file, _, callback) {
var relativeFile = path.relative(file.cwd, file.path);
console.log(relativeFile);
var ext = path.extname(file.path);
if (ext === '.vue') {
var getComponent;
getComponent = function (ast, sourceCode) {
const ta = ast.program.body[0]
if (!babelTypes.isExportDefaultDeclaration(ta)) {
var msg = 'Top level declaration in file ' + relativeFile + ' must be "export default {" \n' + codeFrameColumns(sourceCode, { start: ta.loc.start }, { highlightCode: true });
throw msg;
}
return ta.declaration;
}
var compile;
compile = function (componentName, content) {
var component = vueCompiler.parseComponent(content, []);
if (component.styles.length > 0) {
component.styles.forEach(s => {
const linesToStyle = content.substr(0, s.start).split(/\r?\n/).length;
var msg = 'WARNING: <style> tag in ' + relativeFile + ' is ignored\n' + codeFrameColumns(content, { start: { line: linesToStyle } }, { highlightCode: true });
console.warn(msg);
});
}
var ast = babel.parse(component.script.content, {
parserOpts: {
sourceFilename: file.path
}
});
var vueComponent = getComponent(ast, component.script.content);
vueComponent.properties.push(babelTypes.objectProperty(babelTypes.identifier('template'), babelTypes.stringLiteral(component.template.content)))
var wrapInComponent = babelTemplate("Vue.component(NAME, COMPONENT);");
var componentAst = wrapInComponent({
NAME: babelTypes.stringLiteral(componentName),
COMPONENT: vueComponent
})
ast.program.body = [componentAst]
babel.transformFromAst(ast, null, null, function (err, result) {
if (err) {
callback(err, null)
}
else {
file.contents = Buffer.from(result.code);
callback(null, file)
}
});
}
var componentName = path.basename(file.path, ext);
if (file.isBuffer()) {
compile(componentName, file.contents.toString());
}
else if (file.isStream()) {
var chunks = [];
file.contents.on('data', function (chunk) {
chunks.push(chunk);
});
file.contents.on('end', function () {
compile(componentName, Buffer.concat(chunks).toString());
});
}
} else {
callback(null, file);
}
});
}
var js = {
name: "itools-blocks.js",
path: "wwwroot/assets/js/blocks/*.vue"
}
//
// Compile & minimize js files
//
gulp.task("min:js", function (done) {
gulp.src(js.path, { base: "." })
.pipe(vueCompile())
.pipe(concat("wwwroot/assets/js/blocks/" + js.name))
.pipe(gulp.dest("."))
.pipe(uglify().on('error', function (e) {
console.log(e);
}))
.pipe(rename({
suffix: ".min"
}))
.pipe(gulp.dest("."));
done();
});
any kind of help is well appreciated
The gulpfile with the method “vueCompile” that you’re referring to was specifically written to suit the needs of the internal components we provide in the framework, it’s by no means a silver bullet for all Vue component compilation. However I understand your problem, before writing this code we desperately searched for existing npm-packages that would give us the functionality we needed, but this wasn’t that easy to find as we only use a subset of the features available in Vue.js
We’d be more than happy to get feedback or more information on how this could be done, so we’ll be watching this thread 👍🏼

How can I implement vertical scroll snapping with `IntersectionObserver`?

I am inspired by Rolls Royce website and want to implement the same scroll snapping feature in mine as well, I did it with the HTML default scroll-snap-type which gives me expected behavior but creates two scrollbars, one for the container and another one for the body, which is not expected so I tried to go with the IntersectionObserver but it causes an issue, I can travel to only adjacent slide when directly jumping from 1st slide to 3rd slide.
Here is the code sandbox link: https://codesandbox.io/s/scrollsnap-forked-pre0c?file=/pages/index.vue
Here is the code that I am working
<template>
<main class="landing">
<nav class="scroller">
<ul class="scroller__list">
<li
class="scroller__item"
v-for="(slide, index) in slides"
:key="index"
#click.prevent="scroll(slide.id)"
>
<a
class="scroller__dot"
:href="'#' + slide.id"
#click.prevent="scroll(slide.id)"
></a>
</li>
</ul>
</nav>
<div class="slides-container">
<slide
class="slide"
v-for="(slide, index) in slides"
:key="index"
:img="slide.img"
:id="slide.id"
:format="slide.format"
:filter="slide.filter"
>{{ slide.content }}</slide
>
</div>
</main>
</template>
<script lang="ts">
import Vue from "vue";
export default Vue.extend({
data() {
return {
slides: [
{
img: "car-slide-1.png",
content: "hello world",
id: "car-slide-1",
filter: "color-burn",
},
{
img: "car-slide-2.png",
// promo-video.mp4
content: "Second Car",
id: "car-slide-2",
filter: "color-burn",
// format: "video",
},
{
img: "car-slide-3.png",
content: "Third slide",
id: "car-slide-3",
filter: "color-burn",
},
],
observer: null as any as IntersectionObserver,
options: {
threshold: [0.5],
root: process.browser
? document.getElementsByClassName("slides-container")[0]
: null,
} as IntersectionObserverInit,
};
},
methods: {
scroll(id: string, who: string | null = null) {
console.log("scrolling to ", id, who ? "by " + who : "");
document.getElementById(id)?.scrollIntoView({
behavior: "smooth",
block: "start",
});
},
},
mounted() {
let scrolling = false;
this.observer = new IntersectionObserver((entries, observer) => {
entries.forEach((entry) => {
if (entry.isIntersecting && !scrolling) {
let top = entry.boundingClientRect.top;
scrolling = true;
window.scroll({ behavior: "smooth", top: window.pageYOffset + top });
}
scrolling = false;
});
}, this.options);
document
.querySelectorAll(".slide")
.forEach((slide) => this.observer.observe(slide));
},
});
</script>

font-awesome icon in display of combobox extjs 6

I tried several ways to set an icon, in the displayfield, when an item of the combo is selected with not luck, this is the fiddle for anyone to want try to help with this. very much appreciated any light.
fiddle example
The only solution is to transform the input type combo in a div with this:
fieldSubTpl: [
'<div class="{hiddenDataCls}" role="presentation"></div>',
'<div id="{id}" type="{type}" style="background-color:white; font-size:1.1em; line-height: 2.1em;" ',
'<tpl if="size">size="{size}" </tpl>',
'<tpl if="tabIdx">tabIndex="{tabIdx}" </tpl>',
'class="{fieldCls} {typeCls}" autocomplete="off"></div>',
'<div id="{cmpId}-triggerWrap" class="{triggerWrapCls}" role="presentation">',
'{triggerEl}',
'<div class="{clearCls}" role="presentation"></div>',
'</div>', {
compiled: true,
disableFormats: true
}
],
Override the setRawValue method of the combo like this:
setRawValue: function (value) {
var me = this;
me.rawValue = value;
// Some Field subclasses may not render an inputEl
if (me.inputEl) {
// me.inputEl.dom.value = value;
// use innerHTML
me.inputEl.dom.innerHTML = value;
}
return value;
},
and style your fake combo div like you want.
Thats because an input on HTML can't have HTML like value inside it.
Keep attenction, the get Value method will return you the HTML inside the div, and maybe you should also override it, but thats the only one method.
You will be able to get the selected value with this method:
Ext.fly(combo.getId()+'-inputEl').dom.innerHTML.replace(/<(.|\n)*?>/gm, '');
If I were you I would like to do something like this:
combo.getMyValue();
So add this property to your combo:
getMyValue:function(){
var combo=this;
if(Ext.fly(combo.id+'-inputEl'))
return Ext.fly(combo.id+'-inputEl').dom.innerHTML.replace(/<(.|\n)*?>/gm, '');
},
Here is a working fiddle
Perhaps my solution is similar to a hack, but it works in 6.7.0 and is a bit simpler.
Tested in Chrome. Theme - Material. For another theme will require minor improvements.
Sencha Fiddle live example
Ext.application({
name: 'Fiddle',
launch: function () {
var store = new Ext.data.Store({
fields: [{
name: 'class',
convert: function (value, model) {
if (value && model) {
var name = value
.replace(/(-o-)|(-o$)/g, '-outlined-')
.replace(/-/g, ' ')
.slice(3)
.trim();
model.data.name = name.charAt(0).toUpperCase() + name.slice(1);
return value;
}
}
}, {
name: 'name'
}],
data: [{
class: 'fa-address-book'
}, {
class: 'fa-address-book-o'
}, {
class: 'fa-address-card'
}]
});
var form = Ext.create('Ext.form.Panel', {
fullscreen: true,
referenceHolder: true,
items: [{
xtype: 'combobox',
id: 'iconcombo',
queryMode: 'local',
editable: false,
width: 300,
valueField: 'class',
displayField: 'name',
store: store,
itemTpl: '<div><i class="fa {class}"></i> {name}</div>',
afterRender: () => {
var component = Ext.getCmp('iconcombo');
var element = document.createElement('div');
element.className = 'x-input-el';
element.addEventListener('click', () => component.expand());
component.inputElement.parent().dom.prepend(element);
component.inputElement.hide();
component.addListener(
'change', (me, newValue, oldValue) => {
component.updateInputValue.call(me, newValue, oldValue);
},
component
);
var method = component.updateInputValue;
component.updateInputValue = (value, oldValue) => {
method.call(component, value, oldValue);
var selection = component.getSelection();
if (selection) {
element.innerHTML =
'<div><i class="fa ' + selection.get('class') + '"></i> ' + selection.get('name') + '</div>';
}
};
}
}, {
xtype: 'button',
text: 'getValue',
margin: '30 0 0 0',
handler: function (component) {
var combo = Ext.getCmp('iconcombo');
alert(combo.getValue());
}
}]
});
form.show();
}
});

Why is there no space between form-inline 's elements?

Demo: http://jsfiddle.net/sunnycpp/MPACc/4/
Same code copy-pasted here:
HTML
<div class="container">
<debug-bar ng-controller="parentController">
<debug-bar ng-controller="childController">
<debug-bar ng-controller="oneMoreChildController"></debug-bar>
</debug-bar>
<debug-bar ng-controller="oneMoreChildController"></debug-bar>
</debug-bar>
</div>
Javascript
var angModule = angular.module('components', []);
angModule.directive('debugBar', function () {
return {
restrict: 'E',
template:
'<div>'+
'<form class="form-inline">' +
'<input type="text" class="input-small" ng-model="myText"/>' +
'<button class="btn btn-primary">Broadcast</button>' +
'<button class="btn btn-primary">Emit</button>' +
'</form>' +
'<div ng-transclude></div>'+
'</div>',
transclude: true,
replace: true
};
});
function createController(myText) {
return function ($scope) {
$scope.myText = myText;
$scope.$on("event", function (senderText) {
console.log("Event received in:" + $scope.myText + " from Sender:" + senderText);
});
$scope.$broadCastEvent = function () {
$scope.$broadcast("event", $scope.myText);
console.log("Sent event from:" + $scope.myText);
};
};
}
angModule.controller("parentController", createController("In parent"));
angModule.controller("childController", createController("in FirstChild"));
angModule.controller("oneMoreChildController", createController("in oneMoreChildController"));
angModule.controller("oneMoreChildController", createController("in secondLevelChild"));
Because you didn't define any. :p jsfiddle and bootstrap reset the margins and you don't define any...
Defining some margins fixes the problems immediately
input,button{
margin:0 5px;
}
Example

Knockout js: Add Class to each nth item

Is there any simple way to do something like jquery i.e.
$('#image-gallery li:nth-child(3)').addClass('third-image-child')
but in the context of knockout, I am completely new to knockout but can't seem to find a simple way of doing something which seems like it should be so simple? I thought it might be something to do with finding the 3rd item in the observable array and adding the class but am not sure of the syntax. Help??!
My model is below, it is a simple pagination model, that loads 9 items, and then has next and previous buttons. For now I have added a simple function to generate 100 items just to test it out.
members.DisplayGallery = function(jsondata) {
var viewModel = {
fields: ko.observableArray(jsondata),
pageSize: ko.observable(9),
pageIndex: ko.observable(0),
previousPage: function() {
this.pageIndex(this.pageIndex() - 1);
},
nextPage: function() {
this.pageIndex(this.pageIndex() + 1);
}
};
viewModel.maxPageIndex = ko.dependentObservable(function() {
return Math.ceil(this.fields().length / this.pageSize()) - 1;
}, viewModel);
viewModel.pagedImages = ko.dependentObservable(function() {
var size = this.pageSize();
var start = this.pageIndex() * size;
return this.fields.slice(start, start + size);
}, viewModel);
ko.applyBindings(viewModel, $('#image-gallery')[0]);
};
$(function() {
var data = [];
for (var i = 0; i < 100; i++) {
data.push({
imageLink: "http://sample-image.jpg",
imagePageLink: "http://",
imageTitle: "Title here" + i,
userFullName: "Name" + i,
imageDate: "Description" + i
})
}
members.DisplayGallery(data);
});
Markup:
<ul data-bind="foreach: pagedImages" id="image-gallery">
<li>
<div class="image-thumb" data-bind="style: { backgroundImage: 'url(' + imageLink +')'}">
<a class="image-thumb-link" data-bind="attr: { href: imagePageLink}" href="gallery-single.html"></a>
</div>
<div class="image-text">
<a data-bind="attr: { href: imagePageLink}" href="gallery-single.html"><span class="image-title" data-bind="text: imageTitle"></span></a><br />
<span data-bind="text: userFullName">Username</span><br />
<span data-bind="text: imageDate">Image Date</span>
</div>
</li>
</ul>
If you bind to your observable array using the foreach binding, you can use the $index context property to set the class of every 3rd element, like so:
<li data-bind="css: { 'third-image-child': $index() % 3 == 0 }">
...
</li>

Resources