Keeping css attributes in an iterated dart template - css

I'm working on a Dart project where the user is able to add new custom elements at the click of a button. Each custom element is a div containing a table. The divs are resizeable by the user. My problem is that after a new element is added to the list of elements held by my dart file, the sizes of all the divs are automatically reset. Is there any way to add new elements to the template while keeping the attributes of the old ones the same?
Here is my CSS code that deals with the main divs in my custom element:
#superDivContainer {
border: 2px solid black;
resize: both;
display: inline-block;
}
#tableContainer {
height: 125px;
overflow: scroll;
}
Thanks in advance for the help!

When you change the backing list, the template reiterates, destroying all the old elements and recreating them from scratch, so all user resize information is lost.
When you add a new item to the list, you can store all the sizing information of the old elements and then after the item is added set the new elements size:
// Stores size information
List<List> sizes = [];
void add() {
// Store the old sizes
sizes.clear();
ElementList divs = queryAll(".superDivContainer");
for(int i = 0; i < divs.toList().length; i++) {
Element div = divs[i];
sizes.add([div.style.width, div.style.height]);
}
// Add the new item
yourList.add("new");
// Set the sizes of the new elements
Timer timer = new Timer(new Duration(milliseconds: 1), () {
divs = queryAll(".superDivContainer");
for(int i = 0; i < divs.toList().length && i < sizes.length; i++) {
Element div = divs[i];
div.style.width = sizes[i][0];
div.style.height = sizes[i][1];
}
});
}
Two notes:
I changed superDivContainer to be a class instead of an id since it seems that you are applying it to multiple elements; you will need to change your CSS reflect that
The 1 millisecond timer gets around the fact that there is a tiny delay until the new elements are added and accessible

Related

Create a circular or triangular shaped board

I'm trying to create a game board ( similar to a chess board) for a game in react, and I would like to arrenge the cells (rectangular divs, that take they're value from a state array) in different shapes ( circle, triangle, oval etc) thus creating a board of that shape.
I only know how to arrange divs with CSS flex or grid and that is always rectangular formation (as far as I know)
How can I achieve this?
Any help will be appreciated, thanks!
To dynamically position elements, you need to arrange them in code. We can do that by creating the elements in code, then setting their position programmatically. I'm working off this jsfiddle
First, the HTML:
<div id="my_container" />
It couldn't get simpler. We just want somewhere we can put our elements. Now, for minimal styling, we've got:
#my_container {
position: relative;
width: 200px;
height: 200px;
background-color: powderblue
}
.cell {
background-color: red;
width: 5px;
height: 5px;
position: absolute;
}
I've added colors so that you can see the elements in the generated output, but they're not necessary. What is necessary is those position lines. Making the #container div relative lets it behave nicely with whatever it's placed in, and making each of the .cell divs absolute causes them to be placed on absolute coordinates relative to their parent, which means we can put them where we want. Ok, what's next?
The cells. I assume your cells already exist, but since I don't have them, I've created code to build these cells, shown below:
const container = document.getElementById("my_container");
const number_of_cells = 30;
const cells = [];
for (var i = 0; i < number_of_cells; i++)
{
const newDiv = document.createElement("div");
newDiv.classList.add("cell");
container.appendChild(newDiv)
cells.push(newDiv);
}
The goal here is to put the cells as children of #my_container and also create an array of all the cells. (I think maybe this step is redundant but I don't care...It makes the next step easier.)
function PositionCells(cells, x, y, radius)
{
const incr_angle = (2*Math.PI)/cells.length;
for (let i = 0; i < cells.length; i++) {
var new_x = x+radius * Math.cos(incr_angle * i);
var new_y = y+radius * Math.sin(incr_angle * i);
cells[i].style.left = new_x+'px';
cells[i].style.top = new_y+'px';
}
}
PositionCells(cells, 50,50,30);
Basically what I've done is written a function to position all the cells in an ordered, programmatic way. If you want other shapes, you can pretty easily do the math to come up with where to position them.
And that's it!

How To Prevent CSS Applied to One Three.JS Scene From Being Applied To All Scenes

This may be more of a "you're going to have to do it the long way" type of deal, but here it goes...
When I apply CSS to control the opacity of only one HTML element that is the container of a Three.JS scene in where there are multiple elements that each are a container for their own scene, the CSS applied (even at an inline level) to only one of those elements containing a scene is being applied to all elements that contain a scene and not specifically the one targeted. This happens with any post applied CSS attribute, not just opacity.
The reason why I am attempting this approach at controlling opacity this way is that per my research there is no direct way to set an opacity on a Three.JS group object that contains 1 to N number of meshes. I am (in theory) trying not to have to define all materials with transparency set to "true" and then having to do recursive updates to all meshes in a Three.JS Group object where an animation would fade in/out.
Some of the group objects I'm working with will have many meshes defined in them. Thus, rather than update the opacity of each individual mesh itself contained within a Three.JS group object, my goal was/is to have individual scenes for each type of animation that is capable of having any amount of transparency applied to it run as is then just adjust the HTML element containing that animation's opacity property.
I've tried using one camera and multiple cameras to no avail. I've also tried nesting the containers in one additional element and setting CSS on the parent element but the same issue occurs. I have not tried using multiple renderers as from what I gather in research is that doing so is frowned upon and can lead to performance issues and context limits. The render loop also has "autoClear" set to false so that all scenes render together.
Here is the HTML syntax. You will notice that the first element has a inline style for opacity set to 0.5 and the second element has no inline styling applied:
<div class="three-js-container" id="scene-container-1" style="opacity:0.5;"></div>
<div class="three-js-container" id="scene-container-2"></div>
Here is the Javascript code:
/* Only one renderer instance is created */
var universalRenderer = new THREE.WebGLRenderer({antialias: true, alpha:true});
/* references to all containers are made */
var containerForScene1 = document.getElementById("scene-container-1");
var containerForScene2 = document.getElementById("scene-container-2");
/* two different cameras are created */
var cameraForScene1 = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.001, 1000);
var cameraForScene2 = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.001, 1000);
/* two different scenes are created, one for each element container */
var scene1 = new THREE.Scene();
scene1.userData.element = containerForScene1;
var scene2 = new THREE.Scene();
scene2.userData.element = containerForScene2;
/* the renderer is applied to both scene containers */
containerForScene1.appendChild(universalRenderer.domElement);
containerForScene2.appendChild(universalRenderer.domElement);
When both animations are played, both scenes are rendered at 1/2 opacity rather than just the first scene itself.
Is there a reason why CSS styling applied to one HTML scene containing element is applied to all of the other scene containing elements? Will I just have to suck it up and go the long way around in controlling mesh opacity?
Thanks.
Setting transparency for a THREE.Group:
A Group is just a container. As such, it has children, which are potentially other Groups. But you can only apply transparency to a Material, which is assigned at the Mesh level, not Groups. However, not all is lost, because you can monkey patch Group to allow you to perform the operation seamlessly.
// MONKEY PATCH
Object.defineProperty(THREE.Group.prototype, "transparent", {
set: function(newXP) {
this.traverse(node => {
if (node.material) {
node.material.transparent = newXP
node.material.opacity = (newXP) ? 0.5 : 1
}
})
}
})
// Set up the renderer
const renderer = new THREE.WebGLRenderer({
alpha: true,
antialias: true
})
document.body.appendChild(renderer.domElement)
renderer.setSize(window.innerWidth, window.innerHeight)
const scene = new THREE.Scene()
const size = new THREE.Vector2()
renderer.getSize(size)
const camera = new THREE.PerspectiveCamera(28, size.x / size.y, 1, 1000)
camera.position.set(0, 20, 100)
camera.lookAt(scene.position)
scene.add(camera)
camera.add(new THREE.PointLight(0xffffff, 1))
function render() {
renderer.render(scene, camera)
}
const axis = new THREE.Vector3(0, 1, 0)
function animate() {
requestAnimationFrame(animate)
camera.position.applyAxisAngle(axis, 0.005)
camera.lookAt(scene.position)
render()
}
animate()
// Populate the scene
const cubeGeo = new THREE.BoxBufferGeometry(5, 5, 5)
let opaqueCubes = []
let transparentCubes = []
const randomInRange = () => Math.random() * ((Math.random() <= 0.5) ? -10 : 10)
const opaqueGroup = new THREE.Group()
scene.add(opaqueGroup)
for (let i = 0; i < 10; ++i) {
opaqueGroup.add(new THREE.Mesh(cubeGeo, new THREE.MeshPhongMaterial({
color: "red"
})))
opaqueGroup.children[i].position.set(randomInRange(), randomInRange(), randomInRange())
}
const transparentGroup = new THREE.Group()
scene.add(transparentGroup)
for (let i = 0; i < 10; ++i) {
transparentGroup.add(new THREE.Mesh(cubeGeo, new THREE.MeshPhongMaterial({
color: "green"
})))
transparentGroup.children[i].position.set(randomInRange(-10, 10), randomInRange(-10, 10), randomInRange(-10, 10))
}
// Control the transparency from the input
const xparent = document.getElementById("xparent")
xparent.addEventListener("change", (e) => {
transparentGroup.transparent = xparent.checked
})
html,
body {
padding: 0;
margin: 0;
overflow: hidden;
}
#control {
position: absolute;
top: 0;
left: 0;
padding: 10px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/105/three.js"></script>
<div id="control">
<label>Make the green ones transparent:<input id="xparent" type="checkbox" /></label>
<div>

Make table row behave like an accordion

I'm using a table to display assignments. The table row has an initial height value of 300px. When the table is loaded, the table row height is reduced to 50px so that some of the details are hidden and this is done by adding a style.
.hidedetails {
max-height: 50px;
}
To show the hidden details, the user needs to click on the down-arrow. By doing that, the .hidedetails style is removed and the following is added, at the same time the down-arrow changes to an up-arrow.
.showdetails {
min-height: 300px;
}
To hide the details again, the user simply clicks on the up-arrow, which then changes again to the down-arrow. The result is the following:
I would like to know how can I hide the details of the table row that is showing them if I click on the down-arrow of the table row that is not selected. I have tried with JQuery but it won't work.
In case anyone is interested, I was able to achieve what I wanted by adding this to the onClick event of the arrow icon which is a button widget.
var tableRow = widget.parent.parent;
var rows = tableRow.parent.children._values;
if (widget.text === ("keyboard_arrow_down") ) {
for (var i = 0; i < rows.length; i++) {
if (rows[i].name.indexOf('Table2Row') > -1) {
rows[i].styles = ['hidedetails'];
rows[i].descendants.Button3.text = "keyboard_arrow_down";
}
}
tableRow.styles = ['showdetails'];
widget.text = "keyboard_arrow_right";
} else if (widget.text === ("keyboard_arrow_right")){
tableRow.styles = ["hidedetails"];
widget.text = "keyboard_arrow_down";
}

How to achieve an smooth height transition when adding new elements to a Panel in GWT?

I have a FocusPanel that when gets clicked adds new Elements/Widgets to itself making its height increase. Note, that there is no explicit change of height in the css of the FocusPanel, the height just increases as the result of adding new elements inside the panel
I would like that increase in height to occur through a smooth transition but I am not sure how to achieve it.
I tried applying css transition: height 2s; to the FocusPanel and also to all the other Elements/Widgets that I add to it. But it does not seem to work, there is no transition at all. I assume it is because the height does not increase as a result of me changing the css property but rather by just adding more elements to a container.
What is the right way to achieve a smooth transition of height when adding new elements programatically to a Panel? Thanks!
PS. A good example of what I would like to achieve is the way twitter handles the transition of the panel height when one clicks on a twit.
CSS animations only work if you set the height to fixed values.
A way is to create your own implementation of panel, and override the add method, so as it takes care of computing height and setting it before and after animation time.
As #fascynacja points in its comment, I would go with gwtquery to do that, because of different reasons, but main one is that it is a lightweight library developed in gwt which allows you doing a lot of things with few code lines.
Here you have an example of a panel doing what you want using gquery animations.
import static com.google.gwt.query.client.GQuery.*;
[...]
// Create your own implementation of a panel
public static class MyFlowPanel extends FlowPanel {
// The GQuery object for this panel
GQuery $this = $(this);
// Override the add method so as each time it is called, we run an animation
// You can do the same with the remove method.
#Override
public void add(Widget w) {
// Compute the actual height
int hInitial = $this.height();
// Set height to auto before adding the new child.
$this.height("auto");
// Add the new widget to panel
super.add(w);
// Compute the new height
int hFinal = $this.height();
// Use Gquery to .animate the panel from the old to the new height
// You could replace this with css3 transitions
$this.height(hInitial)
.stop(true)
.animate("height: " + hFinal, 2000);
};
};
public void onModuleLoad() {
// Create your panel, and use it as usual in GWT
final FlowPanel myFlowPanel = new MyFlowPanel();
RootPanel.get().add(myFlowPanel);
// Set some css properties to your panel. You could set these in your style-sheet.
$(myFlowPanel).css($$("border: 1px solid grey; border-radius: 8px; background: #F5FFFA; width: 500px; padding: 8px"));
// Add 10 labels to the panel in periodes of 1000 ms
Scheduler.get().scheduleFixedPeriod(new RepeatingCommand() {
int c = 10;
public boolean execute() {
if (c-- > 0) {
myFlowPanel.add(new Label(c + " Lorem ipsum dolor sit amet, consectetur adipiscing elit."));
return true;
}
return false;
}
}, 1000);
}

Floated image to left of a ul is ignoring margin/padding

I have a paragraph followed by an unordered list, with several list items. I also have an image floated to the left of that. The problem I am having is that the list item margin/padding is being overlapped by that image.
I want the bullets that are next to the image to indent like it should.
Here is a test I wrote up for debugging, where you can see my issue in action.
All of this is inside of a CMS, so the image dimensions are variable, as well as the paragraphs and possible lists in the text.
Any solutions?
(See my first comment for pictures.)
ul {
overflow: auto;
}
I'll have the added advantage of not having the list items wrapping around the image.
Add this:
ul{ list-style-position: inside}
That's it!
Another option would be to shift the list to the right with relative positioning:
img+p+ul {
position: relative;
left: 1em;
top: 0;
}
li style="margin-left: 135px;" Worked best for me.
The overflow: auto; looked ok up front but wound up messing with other elements in my HTML.
You can give your list items an overflow property:
li {
overflow: hidden;
}
That will cause the list item to sort of behave correctly: They will display as a square block, continuing where the image ends as well, they donĀ“t flow nicely to the left. The next list item will.
If you don't bother about adding javascript, here is a jQuery script that will add a margin to the ul that overlaps the image so all the list items remain aligned, and then assigns a negative margin to the li's that doesn't overlap.
$(function(){
//Define a context so we only move lists inside a specified parent
var context = $("#test_div");
//Build a list of images position a size
var imgRects = [];
var imgs = $("img.left", context);
imgs.each(function(i){
var pos = $(this).position();
pos.right = pos.left + $(this).outerWidth(true);
pos.bottom = pos.top + $(this).outerHeight(true);
imgRects.push(pos);
});
//Process each li to see if it is at the same height of an image
var lis = $("li", context);
lis.each(function(i){
var li = $(this);
if(li.parent().css('marginLeft') != "0px"){
return; //Already moved
}
var top = li.position().top;
for(var j in imgRects){
var rect = imgRects[j];
if(top > rect.top && top < rect.bottom){
li.parent().css('marginLeft', rect.right);
return;
} else if(li.parent().css('marginLeft') != "0px"){
li.css('marginLeft', -1 * rect.right);
}
}
});
});
I've tested with your demo page and jQuery 1.3.2 and it works on FF3.5 and IE8 because the image is on top of the document. If the image appears in the middle of a ul, the firsts li's will remain padded. If you need to correct this issue leave a comment and will try to update the script.

Resources