I'm trying to drag rows from one DataTable to another. This animation shows what I try to achieve, kind off.
But from the looks of it PrimeVue doesn't seem to support this, or at least I can't figure out why.
Plan B is to use vue.draggable but seeing as I cannot isolate a child row-item from the datatable, I don't think I can wrap it in a draggable component.
The code used in the animation:
<template>
<div class="flex justify-content-center gap-4">
<DataTable :value="cars1" #row-reorder="onRowReorder">
<Column :rowReorder="true" headerStyle="width: 3em" />
<Column field="brand" header="brand"></Column>
<Column field="color" header="Color"></Column>
</DataTable>
<DataTable :value="cars2" #row-reorder="onRowReorder">
<Column :rowReorder="true" headerStyle="width: 3em" />
<Column field="brand" header="brand"></Column>
<Column field="color" header="Color"></Column>
</DataTable>
</div>
</template>
<script setup>
const cars1 = ref([
{ brand: "BMW", color: "Green" },
{ brand: "Volvo", color: "Yellow" },
{ brand: "Audi", color: "Red" },
{ brand: "Ford", color: "Pink" }
]);
const cars2 = ref([]);
function onRowReorder(event) {
const draggedCar = cars1.value[event.dragIndex]
cars2.value.push(draggedCar);
}
</script>
You can build it yourself by using #vueuse's useDropZone utility function and by using the template in the column of the reorder icon.
That way you stil use PrimeVue's dragging logic, but you can intercept it yourself with mousedown and mouseup events in combination with the onDrop callback of the useDropZone utility function. See snippet below.
<template>
<div class="flex">
<DataTable :value="cars1">
<Column :rowReorder="true" headerStyle="width: 3em">
<template #body="{ index, data }">
<i
#mousedown="onHandleClick(index, data)"
#mouseup="clearHandle"
:class="['p-datatable-reorderablerow-handle', 'pi pi-bars']"
></i>
</template>
</Column>
<Column field="brand" header="brand"></Column>
<Column field="color" header="Color"></Column>
</DataTable>
<DataTable ref="dropzoneTable" :value="cars2">
<Column :rowReorder="true" headerStyle="width: 3em" />
<Column field="brand" header="brand"></Column>
<Column field="color" header="Color"></Column>
</DataTable>
</div>
</template>
<script setup>
const cars1 = ref([
{ brand: 'BMW', color: 'Green' },
{ brand: 'Volvo', color: 'Yellow' },
{ brand: 'Audi', color: 'Red' },
{ brand: 'Ford', color: 'Pink' },
]);
const cars2 = ref([]);
const dropzoneTable = ref();
const { isOverDropzone } = useDropZone(dropzoneTable, onDrop);
const draggingRow = ref();
function onHandleClick(index, data) {
draggingRow.value = data;
}
function onDrop() {
cars2.value.push(draggingRow.value);
}
function clearHandle() {
draggingRow.value = null;
}
</script>
https://stackblitz.com/edit/nuxt-starter-pp7mdb?file=app.vue
Related
I'm using react-data-table-component in my project to create a datatable.
However, the checkboxes are appearing too large.
After checking the docs, I found this page - Overidding Styling Using css-in-js with customStyles, and this example:
// Internally, customStyles will deep merges your customStyles with the default styling.
const customStyles = {
rows: {
style: {
minHeight: '72px', // override the row height
},
},
headCells: {
style: {
paddingLeft: '8px', // override the cell padding for head cells
paddingRight: '8px',
},
},
cells: {
style: {
paddingLeft: '8px', // override the cell padding for data cells
paddingRight: '8px',
},
},
};
There are no mentions on there about checkbox styling, so I attempt this:
const customStyles = {
checkbox: {
style: {
maxHeight: '18px',
maxWidth: '18px',
},
},
};
Unfortunately, the checkboxes remained large sized.
How do I solve this so it makes the checkboxes like the size shown in their example in the screenshots?
Screenshots.
Here is how I solved it:
Create a Checkbox component, like so:
const Checkbox = React.forwardRef(({ onClick, ...rest }, ref) =>
{
return(
<>
<div className="form-check pb-5" style={{ backgroundColor: '' }}>
<input
type="checkbox"
className="form-check-input"
style={{ height: '20px', width: '20px' }}
ref={ref}
onClick={ onClick }
{...rest}
/>
<label className="form-check-label" id="booty-check" />
</div>
</>
)
})
Add Checkbox component to DataTable, like so:
<DataTable
title="Products"
columns={columns}
data={ data }
subHeader
subHeaderComponent={subHeaderComponentMemo}
onRowClicked={ handleRowClicked }
selectableRows
selectableRowsComponent={Checkbox} // Pass the Checkbox component only
responsive
persistTableHead
/>
I'm trying to target the span, but haven't been able to do so. I want to be able to change the background color of the span when the checkbox is checked.
<div className={classes.container}>
<input type="checkbox" id="checkboxid" />
<label className={classes.label} htmlFor="checkboxid">
<span className={classes.labelText}>Target</span>
</label>
</div>
container: {
'& input:checked': {
'& label': {
'& $labelText': {
background: 'red'
}
}
}
}
While label is not inside the input but a sibiling you need to make use of the css sibling selector +
In your context
const useSpanStyles = makeStyles({
container: {
'& input:checked + label': {
'& span': {
background: 'red'
}
},
},
label: {},
labelText: {}
});
export function ComponentXYZ(){
const classes = useSpanStyles();
return (
<div className={classes.container}>
<input type="checkbox" id="checkboxid" />
<label className={classes.label} htmlFor="checkboxid">
<span className={classes.labelText}>Target</span>
</label>
</div>
);
}
To be honest with you, if you are using MUI you should've used their components as its easier to compose and build UI with
Here's my suggestion
function ComponentXYZ(){
const [checked, setIsChecked] = useState(false);
const checkedHandler = useCallback(function _checkedHandler(event: any) {
setIsChecked(event.target.checked);
}, []);
return (
<div>
<FormGroup>
<FormControlLabel
control={<Checkbox
checked={checked}
onChange={checkedHandler}
/>}
label={
<Typography style={{ background: checked ? 'red' : 'inherit' }}>
{'Label That change color'}
</Typography>
}
/>
</FormGroup>
</div>
);
}
In my React application, I have a Navigation bar where in there are multiple Tabs, which are created with the use of Marerial UI's AppBar, Tabs and Tab component (in sequence), as below:
function associatedProps(index) {
return {
id: `nav-tab-${index}`,
'aria-controls': `nav-tabpanel-${index}`
};
}
function LinkTab(props) {
const history = useHistory();
const route = props.route;
console.log(props);
return (
<>
<Tab
component="a"
onClick={(event) => {
event.preventDefault();
history.push(route)
}}
{...props}
/>
</>
);
}
const useStyles = makeStyles((theme) => ({
root: {
flexGrow: 1,
backgroundColor: theme.palette.background.paper,
height: theme.navBarHeight
},
tabIndicator: {
backgroundColor: PRIMARY_RED.default
},
tabBar: {
top: '80px'
}
}));
export default function NavTabs() {
const classes = useStyles();
const [value, setValue] = React.useState(0);
const handleChange = (event, newValue) => {
setValue(newValue);
};
return (
<div className={classes.root}>
<AppBar position="fixed" className={classes.tabBar}>
<Tabs
variant=""
classes={{indicator: classes.tabIndicator}}
value={value}
onChange={handleChange}
aria-label="nav tabs example"
>
<LinkTab {...PRIMARY_NAVIGATION.MY_LIST} {...associatedProps(0)} />
<LinkTab {...PRIMARY_NAVIGATION.MY_REQUESTS} {...associatedProps(1)} />
<LinkTab {...PRIMARY_NAVIGATION.REPORT} {...associatedProps(2)} />
</Tabs>
</AppBar>
</div>
);
}
Now herein this setup I wanted my REPORT tab to be aligned right of the App Bar. I do not see any CSS Rule or Prop which in Documentation, which can help me here.
Please suggest how can I achieve this in current setup.
You should set a class for Tabs like this:
const useStyles = makeStyles((theme) => ({
tabs: {
'&:last-child': {
position: 'absolute',
right: '0'
}
}
}));
export default function NavTabs() {
...
return (
<div className={classes.root}>
<AppBar position="fixed" className={classes.tabBar}>
<Tabs
variant=""
classes={classes.tabs}
value={value}
onChange={handleChange}
aria-label="nav tabs example"
>
<LinkTab {...PRIMARY_NAVIGATION.MY_LIST} {...associatedProps(0)} />
<LinkTab {...PRIMARY_NAVIGATION.MY_REQUESTS} {...associatedProps(1)} />
<LinkTab {...PRIMARY_NAVIGATION.REPORT} {...associatedProps(2)} />
</Tabs>
</AppBar>
</div>
);
Tabs do not provide a property to align a specific item to the start or end. But you can leverage css to achieve your result.
Add a className to the item to be right aligned and define a marginLeft property on it
const useStyles = makeStyles((theme) => ({
root: {
flexGrow: 1,
backgroundColor: theme.palette.background.paper,
height: theme.navBarHeight
},
tabIndicator: {
backgroundColor: PRIMARY_RED.default
},
tabBar: {
top: '80px'
},
rightAlign: {
marginLeft: 'auto',
}
}));
export default function NavTabs() {
const classes = useStyles();
const [value, setValue] = React.useState(0);
const handleChange = (event, newValue) => {
setValue(newValue);
};
return (
<div className={classes.root}>
<AppBar position="fixed" className={classes.tabBar}>
<Tabs
variant=""
classes={{indicator: classes.tabIndicator}}
value={value}
onChange={handleChange}
aria-label="nav tabs example"
>
<LinkTab {...PRIMARY_NAVIGATION.MY_LIST} {...associatedProps(0)} />
<LinkTab {...PRIMARY_NAVIGATION.MY_REQUESTS} {...associatedProps(1)} />
<LinkTab {...PRIMARY_NAVIGATION.REPORT} {...associatedProps(2)} className={classes.rightAlign}/>
</Tabs>
</AppBar>
</div>
);
}
Sample working demo
I am trying to pass a parameter to a function by looping through an the array items with v-for.
<template>
<v-app>
<v-app-bar app>
<v-app-bar-nav-icon #click="drawer = !drawer"></v-app-bar-nav-icon>
<v-spacer></v-spacer>
<h1 ref="y"></h1>
</v-app-bar>
<v-content>
<router-view />
<v-navigation-drawer v-model="drawer" class="x">
<v-list-item
v-for="item in items"
:key="item.unidade"
:to="item.link"
:#click="change(item.method)"
>{{item.unidade}}</v-list-item>
</v-navigation-drawer>
</v-content>
</v-app>
</template>
<script>
export default {
name: "App",
data: () => ({
items: [
{ unidade: "IPE", link: "/ipe", method: "IPE" },
{ unidade: "DCSI", link: "/dcsi", method: "DCSI" },
{ unidade: "RT", link: "/rt", method: "RT" }
],
drawer: false
}),
methods: {
change(val) {
console.log(val);
this.$refs.y.innerText = val;
}
}
};
</script>
<style lang="stylus" scoped>
.x {
position: absolute;
}
</style>
I want the parameter in items arrray to be passed to change(val) method giving each v-list-item a distinct event listener.
Then I want h1 with the ref="y" to change it's text based on the v-list-item I click. But so far I am getting the browser error of "Error in render: "TypeError: Cannot set property 'innerText' of undefined""
Instead of setting the innerText of the <h1> you could instead bind the innerText to a reactive variable. You could create a variable in data that could store the selected method and then bind that to the innerText using {{}} syntax. Doing it this way would be more inline with Vue best practices. Let me show you what I mean.
<template>
<v-app>
<v-app-bar app>
<v-app-bar-nav-icon #click="drawer = !drawer"></v-app-bar-nav-icon>
<v-spacer></v-spacer>
<h1 ref="y">{{ selectedMethod }}</h1>
</v-app-bar>
<v-content>
<router-view />
<v-navigation-drawer v-model="drawer" class="x">
<v-list-item
v-for="item in items"
:key="item.unidade"
:to="item.link"
:#click="change(item.method)"
>{{item.unidade}}</v-list-item>
</v-navigation-drawer>
</v-content>
</v-app>
</template>
<script>
export default {
name: "App",
data: () => ({
items: [
{ unidade: "IPE", link: "/ipe", method: "IPE" },
{ unidade: "DCSI", link: "/dcsi", method: "DCSI" },
{ unidade: "RT", link: "/rt", method: "RT" }
],
selectedMethod: '', // Initially blank
drawer: false
}),
methods: {
change(val) {
this.selectedMethod = val; // Update the value of selectedMethod
}
}
};
</script>
<style lang="stylus" scoped>
.x {
position: absolute;
}
</style>
Hope this helps!
I have created a directive for showing the toolbar and it's content:
import {Component} from "#angular/core";
#Component({
selector: 'ved-toolbar',
templateUrl: './ved.toolbar.html'
})
export class VedToolbar {
time: Date;
constructor() {
setInterval(() => {
this.time = new Date();
}, 1000);
}
}
template:
<md-toolbar flex="50" color="primary">
<md-toolbar-row>
<bootstrap-grid>
<row style="margin: 0;">
<column md="1"></column>
<column md="11">
<column md="4">
<span>LOGO Creditas</span>
</column>
<span>Prihlaseny pouzivatel</span>
<column md="4">
<span>{{ time | date: 'HH:mm:ss' }}</span>
</column>
<column md="4">
<span><a routerLink="/doc/detail" routerLinkActive="active">Nápoveda</a></span>
</column>
</column>
</row>
</bootstrap-grid>
</md-toolbar-row>
however, when I use it just like <ved-toolbar></ved-toolbar> in <head></head> It renders just an empty tag, so nothing is shown.
If I place it inside md-sidenav-container it renders but styles are applied wrong, so it's not fixed on top.
However, when I place ved-toolbar's content into <head/> it renders well.
Surely I have added VedToolbar into app's module declarations list.
What is going wrong in here, please?