deleting an item from custom component (mb-bubbles) don't deletes item in items passed as property - lit

My custom element is called "mb-bubbles". It represent a list of things. Each of these things must have at least the attributes "id" (Number) and "text" (String). If the component have the attribute "editable", you should be able to remove items ("blubbles") from it.
Its source code is:
import { LitElement, html, css } from '../vendor/lit-2.4.0/lit-all.min.js';
export class MbBlubbles extends LitElement {
static styles = [
css`
:host {
display: block;
overflow: hidden;
}
.bubble {
font-size: 14px;
line-height: 14px;
padding: 5px 10px;
border-radius: 18px;
background: #fff;
display: block;
float: left;
margin: 4px 2px;
cursor: pointer;
}
.bubble:hover {
background: #def;
}
.bubble>.remove {
display: inline-block;
line-height: 20px;
width: 20px;
border-radius: 10px;
text-align: center;
font-weight: bold;
}
.bubble>.remove:hover {
background: #fff;
}
`
];
static get properties() {
return {
items: { type: Array }, // An array of objects with 2 attributes: "id" (Number) and "text" (String).
editable: { type: Boolean }
};
}
selectItem(item) {
this.dispatchEvent(new CustomEvent('select-item', { detail: { item } }));
}
removeItem(item) {
this.items = this.items.filter(it => it.id != item.id);
}
render() {
return this.items.map(item => html`
<div class="bubble" #click="${() => this.selectItem(item)}">
${item.text}
${this.editable ? html`<span class="remove" #click="${() => this.removeItem(item)}">×</span>` : ''}
</div>
`);
}
}
customElements.define('mb-blubbles', MbBlubbles);
This component is used from outside in this way:
<mb-blubbles .items="${this.items}" editable></mb-blubbles>
${JSON.stringify(this.items)}
As you can see the property this.items seems unmodified.
How to modify the property from inside my custom component and have its changes reflected from the outside?
Is there any directive for that?

The reactive property only works within the component class, that is, updating this.items from within <mb-blubbles> will cause the contents of it to re-render, but it won't cause the parent to re-render, because the reactivity is based on the accessor of the component class itself.
If you need this to be updated in the parent, it would be better for the removeItems() method to dispatch an event, just like you're doing with the selectItem, and have the parent listen for that and update its own this.items.
class MbBlubbles extends LitElement {
...
removeItem(item) {
this.dispatchEvent(new CustomEvent('remove-item', { detail: { item } }));
}
...
}
class ParentElement extends LitElement {
static properties = { items: { type: Array } };
removeItem(event) {
const { item } = event.detail;
this.items = this.items.filter(it => it.id != item.id);
}
render() {
return html`
<mb-blubbles #remove-item=${this.removeItem} .items=${this.items} editable></mb-blubbles>
${JSON.stringify(this.items)}
`;
}
}
Here's a working playground: https://lit.dev/playground/#gist=f61456f62076b849c0af02b2b1c7aff6

Related

How to add custom toggle button in formsflow.ai

I am using latest version of formsflow.ai and I want to create a custom toggle button. How can I do that in formflow.ai. I refer the following documents of formio to do that.
I am expecting to include a custom toggle button in drag and drop mechanism of formsflow.ai
step 1:create the components inside the forms-flow-web in the following order
step 2:Toggle.js code
import React from "react";
import ReactDOM from "react-dom";
import { ReactComponent } from "react-formio";
import settingsForm from "./Toggle.settingsForm";
import ToggleCustomComp from "./Togglechange";
export default class Toggle extends ReactComponent {
/**
* This function tells the form builder about your component. It's name, icon and what group it should be in.
*
* #returns {{title: string, icon: string, group: string, documentation: string, weight: number, schema: *}}
*/
static get builderInfo() {
return {
title: "Toggle",
icon: "square",
group: "basic",
documentation: "",
weight: 120,
schema: Toggle.schema()
};
}
/**
* This function is the default settings for the component. At a minimum you want to set the type to the registered
* type of your component (i.e. when you call Components.setComponent('type', MyComponent) these types should match.
*
* #param sources
* #returns {*}
*/
static schema() {
return ReactComponent.schema({
type: "toggleCustomComp",
label: "ToggleButton"
});
}
/*
* Defines the settingsForm when editing a component in the builder.
*/
static editForm = settingsForm;
/**
* This function is called when the DIV has been rendered and added to the DOM. You can now instantiate the react component.
*
* #param DOMElement
* #returns ReactInstance
*/
attachReact (element) {
let instance;
return ReactDOM.render(
<ToggleCustomComp
ref={(refer) => {instance = refer;}}
component={this.component} // These are the component settings if you want to use them to render the component.
value={this.datavalue} // The starting value of the component.
onChange={this.updateValue}
data={this.data}
disabled={this.disabled}
// The onChange event to call when the value changes.
/>,
element,() => (this.reactInstance = instance)
);
}
/**
* Automatically detach any react components.
*
* #param element
*/
detachReact(element) {
if (element) {
ReactDOM.unmountComponentAtNode(element);
}
}
}
step 3:Toggle.settingsForm.js
import baseEditForm from 'formiojs/components/_classes/component/Component.form';
const settingsForm = (...extend) => {
return baseEditForm([
{
key: 'display',
components: [
{
// You can ignore existing fields.
key: 'placeholder',
ignore: true,
},
]
},
{
key: 'data',
components: [],
},
{
key: 'validation',
components: [],
},
{
key: 'api',
components: [],
},
{
key: 'conditional',
components: [],
},
{
key: 'logic',
components: [],
},
], ...extend);
}
export default settingsForm;
step 4:Togglechange.jsx
import React, {Component} from 'react';
import '../Toggle/toggle.css';
/**
* An example React component this is simply a controlled input element.
*
*/
export default class ToggleCustomComp extends Component {
constructor(props) {
super(props);
this.state = {
value: props.value
}
}
updateCommentData = (event) => {
//const {type} = this.props.component;
this.setState({value: {checked:event.target.checked}}, () => this.props.onChange(this.state.value));
};
render() {
const {disabled, name} = this.props;
let { value } = this.state;
const checked = value?.checked || false;
return (
/*<input type="text" value={value} className={this.props.component.customClassName} onChange={this.setValue}></input>*/
// <input type="checkbox" id="vehicle3" name="vehicle3" value="Boat"></input>
<label class="switch">
<input
name={name}
value={checked}
type="checkbox"
className="form-control"
onChange={(e)=>this.updateCommentData(e)}
disabled={disabled}
/>
<span class="slider round" />
</label>
);
}
};
step 5 :css file
.switch {
position: relative;
display: inline-block;
width: 60px;
height: 34px;
}
.switch input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
-webkit-transition: .4s;
transition: .4s;
}
.slider:before {
position: absolute;
content: "";
height: 26px;
width: 26px;
left: 4px;
bottom: 4px;
background-color: white;
-webkit-transition: .4s;
transition: .4s;
}
input:checked + .slider {
background-color: #2196F3;
}
input:focus + .slider {
box-shadow: 0 0 1px #2196F3;
}
input:checked + .slider:before {
-webkit-transform: translateX(26px);
-ms-transform: translateX(26px);
transform: translateX(26px);
}
/* Rounded sliders */
.slider.round {
border-radius: 34px;
}
.slider.round:before {
border-radius: 50%;
}
step 6:index.js file
import TextAreaWithAnalytics from "./TextAreaWithAnalytics/TextAreaWithAnalytics";
import Toggle from "./Toggle/Toggle";
const components = {
textAreaWithAnalytics: TextAreaWithAnalytics,
toggleCustomComp: Toggle
};
export default components;

What is the use of & in scss of some page

I have not understood the use of '&' in scss of some page and also the scss I have not understood.
some-page.scss
some-page-selector {
& ion-item {
&.item {
//some properties
&[margin-bottom] {
//some property
}
& ion-avatar {
//some property
}
& ion-avatar ion-img,
& ion-avatar img {
//some property
}
}
In Sass, the & (ampersand) references the parent (or chain of parents) of a block of code.
Given this SCSS (similar to what you posted) ...
page {
& ion-item { // => page ion-item
&.item { // => page ion-item.item
color: red;
&[margin-bottom] { // => page ion-item.item[margin-bottom]
margin-bottom: 1em;
}
& ion-avatar { // => page ion-item.item ion-avatar
font-weight: bold;
}
}
}
}
... you will get this compiled CSS:
page ion-item.item {
color: red;
}
page ion-item.item[margin-bottom] {
margin-bottom: 1em;
}
page ion-item.item ion-avatar {
font-weight: bold;
}

Ionic 3 - I want a modal screen not full size

I need a modal page with no full size (80% width, <60% height, centered) to select some items, like an alert control.
How to implement the CSS for this case?
Initialize modal with cssClass
let modal = this.modalCtrl.create(CustomSelectPage, {data: data}, {cssClass: 'select-modal' });
Then add CSS to the class in app.scss
.select-modal {
background: rgba(0, 0, 0, 0.5) !important;
padding: 20% 10% !important;
}
Change the numbers according to your design.
put this code only in your component css file
::ng-deep .sc-ion-modal-md-h {
--width: 90%;
--height: 70%;
}
in TS file:
async MyModal() {
const modal = await this.modalController.create({
component: MyModalPage,
backdropDismiss: true,
cssClass: 'my-modal',
});
return await modal.present();
}
in SCSS file:
.my-modal {
--width: 70%;
--height: 35%;
}
Assign a class to modal (ionic-4 & ionic-5)
this.modalCtrl
.create({
component: ReportEventComponent,
cssClass: 'add-contact-modal'
})
.then(modalEl => {
modalEl.present();
return modalEl.onDidDismiss();
});
put your css code into global.css file
ion-modal.add-contact-modal {
--height: 85%;
--width: 90%;
}
I'm on Ionic 6 and it worked here.
Add in global.scss:
.premio-modal{
// background-color: red;
.modal-wrapper{
background: rgba(0, 0, 0, 0.5) !important;
/* padding: 20% 10% !important; */
width: 70%;
height: 75%;
border-radius: 5px;
}
}
In component:
async showModal(){
const modal = await this.modalController.create({
component: YourComponent,
cssClass: 'premio-modal',
backdropDismiss: true,
});
modal.onDidDismiss().then(
(m: any) => {
}
);
return await modal.present();
}

ng-bootstrap Datepicker: Is there a way to select more than one date at once?

I'm in need of a calendar widget and, since I'm using Bootstrap 4 and ng-bootstrap in my current project, I'd like to know if the ng-boostrap widget support the multiselection of dates in some way.
I've already tried the wijmo Calendar for multiselection, but without success. Otherwise, can you recommend me a datepicker widget that has this capability?
This can help
<p>This datepicker uses a custom template to display days.</p>
<ngb-datepicker
[showWeekNumbers]="true"
[dayTemplate]="customDay"
(dateSelect)="selectOne($event)"
></ngb-datepicker>
<ng-template
#customDay
let-date
let-currentMonth="currentMonth"
let-selected="selected"
let-disabled="disabled"
let-focused="focused"
>
<span
class="custom-day"
[class.focused]="focused"
[class.bg-primary]="isSelected(date)"
>{{ date.day }}</span
>
</ng-template>
<div *ngIf="modelList.length>0">
<h1>Selected dates:</h1>
<pre>{{modelList| json}} </pre>
</div>
TypeScript
import { Component } from '#angular/core';
import {
NgbCalendar,
NgbDate,
NgbDateStruct,
} from '#ng-bootstrap/ng-bootstrap';
#Component({
selector: 'ngbd-datepicker-customday',
templateUrl: './datepicker-customday.html',
styles: [
`
.custom-day {
text-align: center;
padding: 0.185rem 0.25rem;
border-radius: 0.25rem;
display: inline-block;
width: 2rem;
}
.custom-day:hover, .custom-day.focused {
background-color: #e6e6e6;
}
.bg-primary {
border-radius: 1rem;
}
`,
],
})
export class NgbdDatepickerCustomday {
model: NgbDateStruct;
modelList: Array<NgbDateStruct> = [];
constructor(private calendar: NgbCalendar) {}
isSelected = (date: NgbDate) => {
return this.modelList.indexOf(date) >= 0;
};
selectOne(date) {
if (this.modelList.indexOf(date) >= 0) {
this.modelList = this.modelList.filter(function (ele) {
return ele != date;
});
} else {
this.modelList.push(date);
}
console.log(this.modelList);
}
}
Demo stackblitz
Yes, the ng-bootstrap datepicker supports range selection. You'll need to do manual conversion between the NgbDateStruct and a JavaScript Date object.

nvd3 Display 2 Chart

at my work I try to print some graph with nvd3.
But I can only display 1 graph on my page, and I don't understand why the previous graph don't appear.
Could you give me some hint ?
<!DOCTYPE html>
<meta charset="utf-8">
<head>
<link href="lib/css/nv.d3.css" rel="stylesheet">
</head>
<style>
body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
margin: auto;
position: relative;
width: 960px;
}
/**********
* Legend
*/
.nvd3 .nv-legend .nv-series {
cursor: pointer;
}
.nvd3 .nv-legend .nv-disabled circle {
fill-opacity: 0;
}
text {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
form {
position: absolute;
right: 10px;
top: 10px;
}
#chart, #pid svg {
height: 600px;
width: 600px;
}
</style>
<div id="pid">
<svg></svg>
</div>
<div id="chart">
<svg></svg>
</div>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="lib/js/nv.d3.js"></script>
<script>
var divs = ["pid", "chart"];
divs["pid"]= {id:"#pid svg", datam:[
{
values:[
{x:"M",y:1},
{x:"T",y:2},
{x:"W",y:3},
{x:"R",y:3},
{x:"F",y:4},
{x:"S",y:5},
{x:"U",y:6}
],
key:"Apples"
},
{
values:[
{x:"M",y:5},
{x:"T",y:2},
{x:"W",y:6},
{x:"R",y:8},
{x:"F",y:2},
{x:"S",y:4},
{x:"U",y:1}
],
key:"Zebras"
},
{
values:[
{x:"M",y:4},
{x:"T",y:6},
{x:"W",y:5},
{x:"R",y:7},
{x:"F",y:7},
{x:"S",y:2},
{x:"U",y:5}
],
key:"Bananas"
}
], color:['purple', 'black', 'yellow']};
divs["chart"]= {id:"#chart svg", datam:[
{
values:[
{x:"M",y:1},
{x:"T",y:2},
{x:"W",y:3},
{x:"R",y:3},
{x:"F",y:4},
{x:"S",y:5},
{x:"U",y:6}
],
key:"Apples"
},
{
values:[
{x:"M",y:5},
{x:"T",y:2},
{x:"W",y:6},
{x:"R",y:8},
{x:"F",y:2},
{x:"S",y:4},
{x:"U",y:1}
],
key:"Zebras"
}
], color:['red', 'blue', 'green']};
console.log(divs)
var i=0;
var chart = new Array(2);
nv.render = function render(step) {
// number of graphs to generate in each timeout loop
step = step || 1;
nv.render.active = true;
nv.dispatch.render_start();
setTimeout(function() {
var chart, graph;
for (var i = 0; i < step && (graph = nv.render.queue[i]); i++) {
chart = graph.generate();
if (typeof graph.callback == typeof(Function)) graph.callback(chart);
nv.graphs.push(chart);
}
nv.render.queue.splice(0, i);
if (nv.render.queue.length) setTimeout(arguments.callee, 0);
else {
nv.dispatch.render_end();
nv.render.active = false;
}
}, 0);
};
nv.render.active = false;
nv.render.queue = [];
for (var key in divs) {
console.log(i);
nv.addGraph(function(obj) {
if (typeof arguments[0] === typeof(Function)) {
obj = {generate: arguments[0], callback: arguments[1]};
}
nv.render.queue.push(obj);
console.log(nv.render.queue.length);
if (!nv.render.active) {
nv.render();
}
chart[i] = nv.models.multiBarChart().showControls(true).groupSpacing(0.5).color(divs[key]['color']);
chart[i].yAxis
.tickFormat(d3.format(',.1f'));
d3.select(divs[key]['id'])
.datum(divs[key]['datam'])
.transition().duration(500).call(chart[i]);
nv.utils.windowResize(chart[i].update);
return chart[i];
});
i++;
};
// render function is used to queue up chart rendering
// in non-blocking timeout functions
</script>
I hope you colud help me, thanks.