I have an application with nearly 70 items in a <select> that allow multiple selection.
I'm trying to replace its behavior by two lists, where the user can drag/drop between a source list to a target list. The initial select should reflect the moves.
I'm trying to make it generic because I have to reuse the behavior in several pages, sometimes several times in the same page.
What I have by now (I reduced the actual number of items for readability) :
<div style="border: solid 1px red; overflow:auto">
<select name="lst1" multiple="multiple" id="lst1" class="go">
<option value="1">Element N°1</option>
<option value="2">Element N°2</option>
<option selected="selected" value="3">Element N°3</option>
<option value="4">Element N°4</option>
<option value="5">Element N°5</option>
</select>
</div>
<script type="text/javascript">
$(function () {
$(".go").each(function (index, el) {
var source = document.createElement("ul");
var target = document.createElement("ul");
$(el).after(source);
$(el).after(target);
$(source).addClass("sourceItems");
$(target).addClass("targetItems");
$(el + "option:selected").each(function (index2, sub) {
var item = document.createElement("li");
item.textContent = $(sub).text();
$(target).append(item);
});
$(el + "option:not(:selected)").each(function (index2, sub) {
var item = document.createElement("li");
item.textContent = $(sub).text();
$(source).append(item);
});
$(source).sortable({ connectWith : target});
$(target).sortable({ connectWith : source });
});
});
</script>
<style type="text/css">
.sourceItems, .targetItems { list-style-type: none; float:left; margin: 0; padding: 0; margin-right: 10px; background: #eee; padding: 5px; width: 200px; border: solid 1px black; }
.sourceItems li, .targetItems li { margin: 5px; padding: 5px; width: 180px; height: 12px; cursor: move; }
</style>
This code is producing unexpected result :
The "target" list's generated DOM looks like ok :
<ul class="targetItems ui-sortable">
<li>Element N°3</li>
<li>Element N°7</li>
<li>Element N°8</li>
<li>Element N°14</li>
<li>Element N°20</li>
<li>Element N°15</li>
<li>Element N°21</li>
</ul>
but the "source" list's generated DOM is a mess (it contains the page DOM in each element)):
<li>
.sourceItems, .targetItems { list-style-type: none; float:left; margin: 0; padding: 0; margin-right: 10px; background: #eee; padding: 5px; width: 200px; border: solid 1px black; }
.sourceItems li, .targetItems li { margin: 5px; padding: 5px; width: 180px; height: 12px; cursor: move; }
</li><li>
</li><li></li><li>
.sourceItems, .targetItems { list-style-type: none; float:left; margin: 0; padding: 0; margin-right: 10px; background: #eee; padding: 5px; width: 200px; border: solid 1px black; }
.sourceItems li, .targetItems li { margin: 5px; padding: 5px; width: 180px; height: 12px; cursor: move; }
Element N°1
Element N°2
Element N°3
Element N°4
Element N°5
Element N°6
Element N°7
Element N°8
Element N°9
...
Element N°3Element N°7Element N°8Element N°14Element N°15Element N°20Element N°21Element N°23Element N°27Element N°29Element.
I can't see what's wrong. I'd appreciate someone points me the root of the issue.
Here is a jsfiddle that show the issue : http://jsfiddle.net/gsYHb/
[edit] If i reverse the order of creating the source and target elements, the problem occurs on the other list :
var source = document.createElement("ul");
var target = document.createElement("ul");
$(el).after(target);
$(el).after(source);
in this case, this is the target list that is messy.
As you will see in http://jsfiddle.net/stevebeauge/gsYHb/6/, I have been able to solve my issue.
It was due to the $(this + "option:not(:selected)") selector that was returning the whole page.
Replacing this code with $(this).children("option:not(:selected)") make the script works (+ some other minor fixes)
Related
I have a webpage Find a walk which uses Leaflet to enable site visitors to find a walk in the British Isles from a specific location.
The page generates a search box and map.
<div class="search-box">
<input type="text" autocomplete="off" placeholder="Search by city, town or village" />
<div class="result" ></div>
</div>
<div id="walkingBritainMapDivWrap">
<div id="walkingBritainMapDiv" class="walkingBritainMapDiv"></div>
</div>
For all browsers except IOS the lists of results from the search are displayed in front of the map. However with IOS the list of results is hidden behind the map.
I have tried using z-index on the relevant divs but this has no effects. I understand that Laeflet uses z-indez to add layers to the map which m,ight complicate the issue.
The CSS for these divs is :-
.search-box{
width: 300px;
position: relative;
display: inline-block;
font-size: 14px;
margin-bottom: 12px;
}
.search-box input[type="text"]{
height: 32px;
padding: 5px 10px;
border: 1px solid red;
font-size: 14px;
}
.result{
position: absolute;
z-index: 999;
top: 100%;
left: 0;
background-color: white;
}
.search-box input[type="text"], .result{
width: 100%;
box-sizing: border-box;
}
/* Formatting result items */
.result p{
margin: 0;
padding: 7px 10px;
border: 1px solid #CCCCCC;
border-top: none;
cursor: pointer;
}
.result p:hover{
background: #f2f2f2;
}
.walkingBritainMapDiv {
padding-top: 75%;
width: 100%;
border-width: 1px;
border-style: solid;
border-color: rgb(0, 0, 0);
z-index: 0;
}
Any help would be appreciated to solve this problem.
I have a model that called LinksListWidgetModel that contains an IEnumerable property named Links that contains a list of my website links.
In my Razor view, I have two columns (i.e. one for the grid "col-md-8" and one for another tool "col-md-4". I have a foreach loop that adds each link to the grid tool. I am trying to achieve the following look: Matrix View, however the buttons don't wrap after three, it just continues throughout the width of the page. I am using Bootstrap4.x and flexbox.
I tried following the example in this question: How I can make nice looking matrix of buttons with bootstrap 3?, but that didn't fix the issue either.
Here is my code:
LinksListGrid.cshtml:
#if (Model == null)
{
#Html.Partial("_WidgetNotConfigured") }
else
{
<div class="btn-group btn-matrix" role="group">
#foreach (var link in Model.Links)
{
<div class="linkContainer-gridItem">
<button>
<a class="linkContainer-gridItem-link btn btn-default" href="#link.Url">
#if (!string.IsNullOrEmpty(link.Icon))
{
<div>
<i class="icon #link.Icon"></i>
</div>
}
#link.Label
</a>
</button>
</div>
}
</div>
}
LinkContainer.scss:
.linkContainer {
padding: 1rem;
margin-bottom: 2rem;
position: relative;
background: #fff;
border-radius: 8px;
border: 1px solid #dddfe2;
.linkContainer-title {
color: #fff;
background: $colorBrandDarkBlue;
padding: 1rem;
border-radius: 5px;
margin: -.5rem -.5rem 1rem -.5rem;
font-weight: 400;
}
.linkContainer-list {
list-style: none;
padding: 0;
margin-bottom: 0;
font-family: "Montserrat",sans-serif;
.linkContainer-item {
position: relative;
.linkContainer-link {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
padding: .75rem .5rem;
color: $colorBrandBlue;
font-weight: 400;
font-size: 18px;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
text-decoration: none;
background-color: transparent;
font-family: "Montserrat",sans-serif;
}
.icon:before {
font-size: 30px;
margin-right: 1rem;
}
}
.linkContainer-item:after {
content: '';
position: absolute;
top: 0;
right: 0;
left: 0;
border-top: 1px dotted;
opacity: .25;
}
}
.linkContainer-grid {
width: 290px;
display: block;
margin: 0 auto;
padding: 0;
padding-bottom: 19px;
font-family: "Montserrat",sans-serif;
text-align: center;
.linkContainer-gridItem {
background-color: $colorBrandBlue;
border-radius: 25px;
width: 120px;
height: 120px;
margin: 10px;
display: inline-block;
vertical-align: top;
.linkContainer-gridItem-link {
color: white;
text-decoration: none;
display: flex;
flex-direction: column;
justify-content: center;
height: 100%;
padding: 20px 10px;
}
.icon {
font-size: 30px;
margin-bottom: 0.25rem;
}
}
}
#media screen and (max-width: 767px) {
&.linkContainer-gridLayout {
padding: 0;
}
}
}
.btn-matrix {
> .btn {
&:nth-child(3n+4) {
clear: left;
margin-left: 0;
}
&:nth-child(n+4) {
margin-top: -1px;
}
&:first-child {
border-bottom-left-radius: 0;
}
&:nth-child(3) {
border-top-right-radius: 4px !important;
}
&:nth-last-child(3) {
border-bottom-left-radius: 4px !important;
}
&:last-child {
border-top-right-radius: 0;
}
}
}
It ends up looking like this: Grid View
How can I change this to make it look like the first screenshot's matrix/grid display? After every 3 buttons it goes on the second line.
I don't think clear:left; will work on flexbox. But there are other ways to do it.
HTML & SCSS way
I think you would be better off just styling .btn-matrix yourself, without mixing .btn-group styles, because Bootstrap .btn-group brings in lots of noice on its .btn children, such as border-radius, border, etc. You would need to style them using your own styles anyway so why bother.
I think the following structure should be generic enough you can use to construct a matrix:
<div class="btn-matrix btn-matrix-white" role="group">
<a class="btn" href="#">
<div class="fa-stack fa-2x">
<i class="fas fa-circle fa-stack-2x" />
<i class="fas fa-chart-bar fa-stack-1x" />
</div>
Reports
</a>
...
</div>
First of all, in your original HTML structure, you had a button that wraps an anchor link. That's not necessary because Bootstrap has classes to style an anchor link to just look like a button.
Secondly, I am using icon stacking method to construct icons with circle backgrounds. You can read about it here: https://fontawesome.com/how-to-use/on-the-web/styling/stacking-icons
Finally, here is the styles:
$numOfMatrixItemPerRow: 3;
$matrixItemBorderWidth: 1px;
.btn-matrix {
display: flex;
flex-flow: row wrap;
box-shadow: 0 0 1rem #ccc;
border: 1px solid transparent;
border-radius: .25rem;
> .btn {
display: flex;
flex-flow: column nowrap;
align-items: center;
justify-content: flex-start;
flex-grow: 0;
flex-shrink: 0;
padding: 5%;
width: calc(100% / #{$numOfMatrixItemPerRow});
border-radius: 0;
}
&.btn-matrix-white {
> .btn {
background-color: #fff;
border-right: $matrixItemBorderWidth solid #ddd;
border-bottom: $matrixItemBorderWidth solid #ddd;
.fa-stack-2x {
color: var(--primary);
}
.fa-stack-1x {
color: #fff;
}
&:nth-child(#{$numOfMatrixItemPerRow}n) {
border-right: none !important;
}
&:last-child {
border-bottom: none !important;
}
}
}
}
Really nothing tricky there except you would need to use SASS Interpolation to involve $numOfMatrixItemPerRow variable into calculations:
Display .btn-matrix as a wrappable flex row
Set the width of each button to be 100% / $numOfMatrixItemPerRow so that each row will contain the exact number of items before breaking into new rows
Display .btn as flexbox column so that you can easily align the icons and text there
Set the right and bottom border of each button, with exceptions of the last one on each row and last one
demo: https://jsfiddle.net/davidliang2008/1wqost69/201/
ASP.NET MVC way
Now since you're using ASP.NET MVC, I think there is another approach you can consider. That is, in the loop where you loop though each link from the model, you can define a variable there and do something like:
<div class="btn-matrix">
#for (int i = 0; i < Model.Links.Count(); i++)
{
var link = Model.Links[i];
if (i % numOfItemsPerRow == 0)
{
<div class="row">
}
<div class="col-md-4">
<a class="btn" href="#link.Url">
#if (!string.IsNullOrEmpty(link.Icon))
{
<div>
<i class="icon #link.Icon"></i>
</div>
}
#link.Label
</a>
</div>
if ((i+1) % numOfItemsPerRow == 0 || i + 1 == Model.Links.Count())
{
</div>
}
}
</div>
I'm just making things up but hopefully you see where I am going.
I am using jquery-3.4.1.min.js and jquery-ui-1.12.1 .
I have a problem with jquery ui sortable widget.
When I try to drag "div.container", which is child of "div.containerList" (with applied sortable), I am not able to trigger dom change on whole height of sibling element, I have to adjust dragged element vertical position.
Can somebody please explain this behavior, possibly with solution?
Thank you very much!
https://jsfiddle.net/chada090/47ku2jer/
A added some modificaton to make it more "contrast" for each element.
HTML
<div class="containerList">
<div class="container"><span class="title">test kontejner</span>
<div class="addTask">
<div class="switch">Přidat úkol</div>
</div>
<div class="taskList"></div>
</div>
<div class="container">
<span class="title">test kontejner1</span>
<div class="addTask">
<div class="switch">Přidat úkol</div>
</div>
<div class="taskList"></div>
</div>
<div class="container placeholder"><span class="title_newCont">Nový kontejner</span></div>
</div>
JS
$(".containerList").sortable({
// containment: "parent",
placeholder: "ui-state-highlight container",
change: function( event, ui ) {
console.log(ui);
}
}).disableSelection();
CSS
.containerList{
display: flex;
min-height: 600px;
}
.containerList .container{
display:inline;
margin-left: 15px;
margin-right: 15px;
flex: 0 0 300px;
min-height: 150px;
width: 300px;
display:flex;
flex-direction: column;
background: gray;
padding: 20px;
}
.container .title_newCont{
cursor: pointer;
color: blue;
}
.container .addTask{
margin-top: 10px;
margin-bottom: 10px;
background-color: white;
box-shadow: 0px 3.2px 7.2px 0px rgba(0, 0, 0, 0.18), 0px 0.6px 1.8px 0px rgba(0, 0, 0, 0.11);
min-height: 20px;
display: flex;
flex-direction: column;
align-items: center;
padding: 5px;
}
.container .taskList{
margin-top: 20px;
background: brown;
height: 20px;
}
.taskList > div{
background-color: white;
margin-top: 15px;
}
.taskList .taskItem{
margin-bottom: 10px;
background-color: white;
box-shadow: 0px 3.2px 7.2px 0px rgba(0, 0, 0, 0.18), 0px 0.6px 1.8px 0px rgba(0, 0, 0, 0.11);
min-height: 40px;
display:flex;
align-items: center;
padding: 5px;
background: blue;
}
.taskNewItem > *{
padding: 3px;
background: green;
}
.taskNewItem input{
border: none;
outline: none;
background: red;
}
.addTask .switch{
text-align: center;
width: 100%;
background: yellow;
}
.taskNewItem{
display: flex;
flex-direction: column;
}
.taskNewItem input{
width: 100%;
}
.taskNewItem .taskButtonAdd{
width: 100%;
background-color: green;
flex:0 0 40px;
font-size: 15px;
border: none;
}
.ui-state-highlight{
background-color:blue !important;
}
after reading through API documentation, I found option "tolerance", which solved my problem.
tolerance Type: String Default: "intersect" Specifies which mode to
use for testing whether the item being moved is hovering over another
item. Possible values: "intersect": The item overlaps the other item
by at least 50%. "pointer": The mouse pointer overlaps the other item.
Code examples: Initialize the sortable with the tolerance option
specified:
1 2 3 $( ".selector" ).sortable({ tolerance: "pointer" });
I am developing an Angular 7 web application and am struggling with a Mat-Selection-List where I allow the user to drag and drop the mat-list-option items.
Each mat-list-option item comprises a div which uses Flex Layout to arrange its components as follows:
<mat-selection-list #taskGroupSelectionList
cdkDropList
[(ngModel)]="selectedOptions"
(ngModelChange)="onNgModelChange($event)"
(selectionChange)="onSelectionChange($event)"
class="task-group-list"
(cdkDropListDropped)="drop($event)">
<mat-list-option class="task-group-box" checkboxPosition="after" *ngFor="let taskGroup of taskGroups" [value]="taskGroup" cdkDrag>
<!-- Task Group Item -->
<div fxLayout="row" *ngIf="taskGroup" fxLayoutAlign="start center" style="width: 100%; height: 100%;">
<!-- Move Handle -->
<div fxFlex="32px" style="padding: 0 0 0 4px;">
<mat-icon class="summary-channel-handle">menu</mat-icon>
</div>
<!-- Index -->
<div fxFlex="24px;">
<p style="margin: 0; text-align: right;">
{{taskGroup.orderId}}:
</p>
</div>
<!-- Title -->
<div fxFlex="nogrow">
<p style="margin: 0; padding: 0 0 0 8px; text-overflow: ellipsis; white-space: nowrap;">
{{taskGroup.title}}
</p>
</div>
</div>
</mat-list-option>
</mat-selection-list>
The key CSS styles for this simple component are as follows:
.task-group-list {
width: 100%;
height: 100%;
display: block;
background: white;
}
.task-group-box {
border-left: solid 1px #ddd;
border-right: solid 1px #ddd;
border-bottom: solid 1px #ddd;
border-radius: 4px;
height: 48px;
color: rgba(0, 0, 0, 0.87);
box-sizing: border-box;
cursor: move;
background: white;
}
.task-group-box:first-child {
border: solid 1px #ddd;
}
.task-group-list.cdk-drop-list-dragging .task-group-box:not(.cdk-drag-placeholder) {
transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
}
.cdk-drag-preview {
box-sizing: border-box;
border-radius: 4px;
height: 48px;
box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2), 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12);
}
.cdk-drag-placeholder {
box-sizing: border-box;
border-radius: 4px;
height: 48px;
opacity: 0;
}
.cdk-drag-animating {
transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
}
Functionally I can drag and drop the list items, however when dragging, the mat-list-option checkbox which I have placed on the right checkboxPosition="after" moves to the top left corner and pushes the elements of the mat-list-option down.
Does anyone know why the layout changes on dragging please?
The element being dragged can be found as the last child of body in the DOM (only on drag), and this creates quite some problems as you can read here.
If your mat-list-option element is not very complex, only the checkbox and some text, you can solve this by adding some CSS to the global styles.css file, for example:
/* Checkbox and text inline and vertically centered */
.cdk-drag-preview .mat-list-item-content {
display: flex;
align-items: center;
}
/* Checkbox margin from text */
.cdk-drag-preview .mat-pseudo-checkbox {
margin-right: 10px;
}
You can see a DEMO in this stackblitz that I created.
If the content of your mat-list-option element is a bit more complex you will need to inspect the element and add the necessary styles. You can do this by dragging the mat-list-option and right clicking while dragging, inspect element and find classes that you can use to style it.
A better alternative might be to just create a custom cdkDragPreview. You can style this as you wish.
<mat-selection-list #movies cdkDropList>
<mat-list-option *ngFor="let movie of movies" cdkDrag>
{{movie}}
<ng-template cdkDragPreview [matchSize]="true">
<div class="movie-preview">
{{ movie }}
</div>
</ng-template>
</mat-list-option>
</mat-selection-list>
Important: The matchSize input is required to automatically size the item being dragged.
And since the css is scoped to your component, even when it's moved outside the DOM tree of your component it retains the style (assuming Emulated style encapsulation).
.movie-preview
{
line-height: 3;
padding: 0 1em;
color: hotpink;
background: white;
/* border (and hotpink color) are optional, based on your preference */
border: 1px solid #ccc;
border-radius: .5em;
}
That looks like this when dragging:
Try with ::ng-deep
::ng-deep .cdk-drag-preview .mat-list-item-content{
display: flex;
flex-direction: row;
align-items: center;
box-sizing: border-box;
padding: 0 16px;
position: relative;
height: inherit;
}
this works for me
I need a quick solution to getting some decent layout working within the tabnav widget display area. I'm already using blueprint css for other aspects of the layout but I don't have enough CSS chops to figure out how to get blueprint containers to display within the tabnav's content area.
The tabnav's content area is <div class="main_tabnav_content" id="main_tabnav_content">
and this is the related css:
.main_tabnav {
color: #000;
border-bottom: 1px solid #ccc;
margin: 13px 0px 0px 0px;
padding: 0px;
z-index: 1;
padding-left: 10px }
.main_tabnav ul {
margin: 0px;
padding: 0px;
}
.main_tabnav li {
display: inline;
overflow: hidden;
list-style-type: none; }
.main_tabnav li span.disabled {
color: #888888;
background: #FAFAFA;
border: 2px solid #DDDDDD;
border-bottom: none;
padding: 2px 5px 0px 5px;
margin: 0;
text-decoration: none;}
.main_tabnav a, main_tabnav a.active {
color: #000000;
background: #EEEEEE;
font-weight: bold;
border: 1px solid #ccc;
padding: 2px 5px 0px 5px;
margin: 0;
text-decoration: none; }
.main_tabnav a.active {
background: #FFFFFF;
border-bottom: 3px solid #FFFFFF; }
.main_tabnav a:hover {
color: #FFFFFF;
background: #CCCCCC; }
.main_tabnav a:visited {
color: #000000; }
.main_tabnav a.active:hover {
background: #FFFFFF;
color: #000000; }
.main_tabnav_content {
background: #FFFFFF;
padding: 20px;
border:1px solid #ccc;
border-top: none;
z-index: 2;
}
I'd like to be able to put some columns inside the main_tabnav_content area like this
<div class="span-5 colborder">
stuff
</div>
<div class="span-5 colborder">
stuff
</div>
<div class="span-5 colborder">
stuff
</div>
At the moment the columns are displayed but the main_tabnav_content box doesn't expand around them. Any suggestions for how to get the blueprint layout nested inside the main_tabnav_content area?
From Blueprint: screen.css:
.column, div.span-1, div.span-2, div.span-3, div.span-4, div.span-5, div.span-6, div.span-7, div.span-8, div.span-9, div.span-10, div.span-11, div.span-12, div.span-13, div.span-14, div.span-15, div.span-16, div.span-17, div.span-18, div.span-19, div.span-20, div.span-21, div.span-22, div.span-23, div.span-24 {float:left;margin-right:10px;}
.last, div.last {margin-right:0;}
.span-1 {width:30px;}
.span-2 {width:70px;}
.span-3 {width:110px;}
.span-4 {width:150px;}
.span-5 {width:190px;}
and also:
div.colborder {padding-right:24px;margin-right:25px;border-right:1px solid #eee;}
I think if you add this <div class="clearfix"></div> before the end of the main_tabnav_content div it should fix the float problem.
Do you have the css for the span-5 & colborder classes? It sounds like it might be a float problem. Basically what's happening is when you add the columns to the main_tabnav_content div, the div doesn't expand, right?