Hey so I have little drag and drop game written in React konva. My draggable piece looks like this (simplified for convenience
return this.props.areas.map((area) =>
<Image draggable=true
{...area.imageProps}
onDragStart={() => {...}}
onDragEnd={() => {...}}
onDragMove={() => {...}}/> )
I would like the piece that is currently being dragged to be the one on the highest layer (highest zIndex).
Problem is React-Konva does not support zIndexing, and seems to want you to enforce the zIndexing based on order in the render method.
My solution was to updated the reactState with a currentDragIndex
...<Image
onDragStart = {this.setState({currentDragIndex: area.index})}
/>
Then I try to reorder in the render method. However this causes the drag event to be interrupted.
Anyone have a good solution to this?
You chould just use moveToTop() method
onDragStart={(el) => {el.target.moveToTop();}}
If you have an array of some items, the simplest solution is to change that array, by moving the dragging node to the end of the array. In your render function you just need to draw items from the array.
handleDragStart = e => {
const id = e.target.name();
const items = this.state.items.slice();
const item = items.find(i => i.id === id);
const index = items.indexOf(item);
// remove from the list:
items.splice(index, 1);
// add to the top
items.push(item);
this.setState({
items
});
};
Demo: https://codesandbox.io/s/11j51wwm3
Related
Trying to link shapes to slides the same way it's done with images.
.
.
Reason for this request is linking images seems much harder in terms of locating the exact one to be linked.
Have realized it might be best to link shapes through match/search text then insert the images after.
Codes attempted though please ignore if completely irrelevent.
function myFunction(){
var searchText = "IMAGE1";
var presentation = SlidesApp.getActivePresentation();
var slide = presentation.getSlides()[4];
// 2. Replace the shape which has the text of "searchText" with the image of "imageUrl".
slide.getShapes().forEach(s => {
if (s.getText().asString().toLocaleUpperCase().includes(searchText.toLocaleUpperCase())) {
s.setLinkSlide('INSERT_SLIDE_LINK');
}
}
)
}
Slides Example
Thank you
I believe your goal is as follows.
You want to link the shape to a slide by searching the text in the shape on Google Slides.
You want to achieve this using Google Apps Script.
setLinkSlide can use Slides Object. I thought that this might be able to be used.
Sample script:
function myFunction() {
const obj = { text1: 3, text2: 3, text3: 4, text4: 5, text5: 4, text6: 3 }; // This is from your showing sample image.
const slides = SlidesApp.getActivePresentation().getSlides();
slides.forEach(s => {
s.getShapes().forEach(shape => {
const t = obj[shape.getText().asString().toLowerCase().trim()];
if (t) {
shape.setLinkSlide(slides[t - 1]);
}
});
});
}
Note:
This sample script is for your provided sample Google Slides. When you change this, please modify obj. Please be careful about this.
Reference:
setLinkSlide(slide)
SUGGESTION
If I've understood your post correctly, these are your goals:
Check the text on each of the shapes in your slides
See if the current text shape matches the current searchText value
If true, link a dedicated slide to the current shape.
Sample Tweaked Script
function sample() {
var presentation = SlidesApp.getActivePresentation();
var slide = presentation.getSlides();
//Define the 'searchText' value & it's dedicated slide to be linked
var find = {
search: [["Text1", slide[1]],
["Text2", slide[2]],
["Text3", slide[3]]]
};
find.search.forEach(d => {
let searchText = d[0];
let linkSlide = d[1];
slide[0].getShapes().forEach(s => {
s.getText().asString().trim().toLowerCase() === searchText.toLowerCase() ? s.setLinkSlide(linkSlide) : null;
});
})
}
I'm building a React Native App using Hooks and ES6.
The Home screen fetches some data from API and display it as image galleries.
The Home screen is the parent component which includes a slideshow gallery and a Flatlist as children.
1) Slideshow - image gallery autoplay that run some operations (fetching URL in case user clicks on them) on each image iteration (every 3 secs) - This causes the re-render on the entire parent components and its children but it renders every 3 secs as supposed
2) Flatlist - just a simple Flatlist that takes an array of images - this should only rendered once when the parent component is initially loaded first
The problem
Every 3 seconds a new image is displayed in the slideshow, a function runs to fetch some props for the image displayed, the parent component is re-rendered but there is no need for Flatlist in the second gallery to run again since the array of images is the same as initially loaded (not changed)
This is my Parent code
const getTopTenMovies = (state) => state.moviescreen.popularmovies //fetching data from Redux
const upcomingMovies = (state) => state.moviescreen.upcomingmovies
const MoviesScreen = (props) => {
const topTenMovies = useSelector(getTopTenMovies);
const [imageIndex, setImageIndex] = useState("");
/* useEffect below runs every time imageIndex changes (a new image is displayed every 3 secs) */
useEffect(() => {
setCarouselMovieId(selectedImageItem.id);
setCarouselMovieTitle(selectedImageItem.title);
}, [imageIndex]);
/* user clicks on an image and is taken to another screen
const fetchMovieHandler = async (movieId, movieTitle) => {
props.navigation.navigate({
routeName: "MovieDetail",
params: {
assetId: movieId,
assetName: movieTitle,
},
});
};
return (
<ImageGallery
data={topTenMovies}
currentImage=setImageIndex(index)
...
/>
<View style={styles.containerRow}>
<FlatList
horizontal={true}
data={upcomingMovies}
initialNumToRender={5}
windowSize={10}
removeClippedSubviews={true}
keyExtractor={(item) => item.id.toString()}
renderItem={(itemData) => (
<HomeItem
id={itemData.item.id}
poster={itemData.item.poster_path}
onAssetSelection={fetchMovieHandler}
/>
)}
/>
</View>
</View>
)
}
The component renders the individual images within the Flatlist and passes the parameters (movieId, movieTitle) to the fetchMovieHandler function.
HomeItem code below...
class HomeItem extends React.PureComponent {
render() {
const assetHandler = async () => {
this.props.onAssetSelection(this.props.id, this.props.title);
};
console.log("HomeItem is called");
return (
<TouchableOpacity onPress={assetHandler}>
<View>
<Image
key={this.props.id}
source={{
uri: `https://*******${this.props.poster}`,
}}
/>
</View>
</TouchableOpacity>
);
}
}
export default React.memo(HomeItem);
Every 3 seconds the HomeItem is called and I see 10 log entries (one for each image and render - upcomingMovies has 10 images). From the UI everything looks fines since the images dont seems to change probably because I've defined the HomeItem as PureComponent and using React.memo(HomeItem) but it is causing performance issues since I'm getting the following error:
VirtualizedList: You have a large list that is slow to update - make sure your renderItem function renders components that follow React performance best practices like PureComponent, shouldComponentUpdate, etc
I tried to include the Flatlist inside the HomeItem component but the problem persists.
Following further research and thanks to #landorid suggestion I figured out that I need control the renderItem of the FlatList by declaring it in a separate function with a useCallback hook and dependency on my data source.
Tried to include Flatlist in a separate PureComponent my the re-rendering kept happening.
The ideal solution is to change this code
renderItem={(itemData) => (
<HomeItem
id={itemData.item.id}
poster={itemData.item.poster_path}
onAssetSelection={fetchMovieHandler}
/>
)}
to something like this
.....
const renderRow = ({ itemData }) => {
return (
<HomeItem
id={itemData.item.id}
poster={itemData.item.poster_path}
title={itemData.item.title}
onAssetSelection={fetchMovieHandler}
/>
);
};
keyExtractor = (item) => item.id.toString();
return (
<FlatList
horizontal={true}
data={upcomingMovies}
initialNumToRender={5}
windowSize={10}
removeClippedSubviews={true}
keyExtractor={keyExtractor}
renderItem={renderRow}
/>
and then wrapping the renderRow() function in a useCallback hook.
The problem is that my data source data={upcomingMovies} is an array of objects with 2 levels and I need to use itemData.item.title to retrieve the title value for example.
While it works in my current renderItem settings it won't work when I use it in an external function as in
const renderRow = ({ itemData }) => {
return (
<HomeItem
id={itemData.item.id}
poster={itemData.item.poster_path}
title={itemData.item.title}
onAssetSelection={fetchMovieHandler}
/>
);
};
I simply get, "item" variable not available
Any suggestion on how to modify the function?
If I were you I would change the following:
Don't place inline functions in FlatList (keyExtractor, renderItem)! Place it to separated function.
Use simple Component extension instead of PureComponent and compare your props with `shouldComponentUpdate. (I think the passed function called onAssetSelection causes the re-render. Check it.)
You don't need to use React.memo because it is for functional components.
I have the following which displays the chart, however when I adjust the handles on the navigator the chart doesn't adjust to display the points for the selected period.
This is my Razor code:
#(Html.Kendo().StockChart<Portal.Models.Company.StockDataPoint>(Model.StockPricePerformance)
.Name("stockChart")
.DateField("Date")
.Series(series =>
{
series.Line(s => s.ClosingPrice)
.Markers(false);
})
.CategoryAxis(axis =>
{
axis.Categories(model => model.Date).Type(ChartCategoryAxisType.Category)
.MajorGridLines(lines => lines.Visible(false))
.Axis.BaseUnit = ChartAxisBaseUnit.Months;
axis.Labels(label => label.Step(24).Rotation(-60).Format("dd MMM yy"));
axis.Axis.BaseUnitStep = 10;
})
.Navigator(nav =>
{
nav.Series(series => series.Area(data => data.ClosingPrice));
nav.DateField("Date");
nav.AutoBind(true);
})
.Tooltip(tooltip => tooltip
.Visible(true)
.Template("#= dataItem.ClosingPrice # [#=dataItem.Date#]")
)
)
This is what the chart looks like:
As you can see the chart hasn't been redrawn or adjusted to only show the points for the chosen period. I had a look at the Telerik examples and API reference but I couldn't work out what options would make the navigator work.
So I figured it out.
Instead of using axis.Categories(model => model.Date).Type(ChartCategoryAxisType.Category) I used axis.Axis.BaseUnit = ChartAxisBaseUnit.Days; in the CategoryAxis part of the chart definition.
For my chart this was enough for the chart to show all points (no aggregation) and allow the navigator to work without any extra work.
I've the this Shadow Element/root in this example http://jsfiddle.net/fyf6thte/8/ working perfectly with JavaScript, interested to have similar one with DART, so I wrote the below code (using the same html and css file), but I could not see the button it looks theshadow.innerHTML = '<button id="d">click</button>'is not working
the full code is:
import 'dart:html';
void main() {
var thehost = document.querySelector('#host1');
document.registerElement(fonixDiv.tag, fonixDiv);
thehost.append(new fonixDiv());
}
class fonixDiv extends HtmlElement {
static final tag = 'fonix-div';
var shadow;
bool disabled;
factory fonixDiv() => new Element.tag(tag);
fonixDiv.created() : super.created() {
shadow = this.createShadowRoot();
shadow.host.innerHTML = '<button id="d">click</button>';
shadow.host.onClick.listen((e){
this.host.dataset.disabled='true'; // set Attribute to the custom element
});
shadow.children.d.onClick.listen((e){
this.text = "you clicked me :(";
// or shadow.children[0].textContent="Shadow DOM content changed";
this.disabled=true;
// alert("All: button, text and host should be change");
});
}
#override
void attached() {
super.attached();
this.disabled=disabled;
}
}
I'm not sure about the accuracy of the balance of the code, I can check it only after I see the button.
any help.
The error is correct: in Dart 'this' is not bound contextually as in JS and instead we have lexical scoping;
in your dart code you are actually changing the text content of the custom element and not of the target of the event (the button in the shadow root). So basically you have a custom element, you set the text content on it but you also have a shadow root created inside of that same DOM node and it shadows everything else you put inside that custom element and that is why you do not see it and continue to see the shadow root's content - this is how shadow root works by design.
To fix it you need to update the text content (and the disabled property) on the button (for example e.target.text = ...).
Hope this helps.
Seems like the .host should be removed from this line
shadow.host.innerHTML = '<button id="d">click</button>';
shadow.innerHTML = '<button id="d">click</button>';
The jsfiddle doesn't have it and it seems weird. I think with .host you add it basically to this and therefore as child not as content.
I think the main issue is: Use innerHtml instead of innerHTML.
There are a few additional minor things you need to fix:
Remove 'host', as Gunter says, you want to set the innerHtml of the shadow.
Instead of shadow.children.d.onClick, do shadow.querySelector('#d').onClick.
Also, do dataset['disabled'] instead of dataset.disabled.
I'm using the angularjs ui-grid and I have a "total" row that I want to pin to the top of the grid no matter what is the current sorting.
How can I accomplish that?
I think this is what you are looking for : Row Pinning
Essentially add another hidden column, something like this:
{
field: 'pinned',
visible: false,
sort: {direction: uiGridConstants.ASC, priority: 0}, //use uiGridConstants.DESC for pinning to the bottom
suppressRemoveSort: true,
sortDirectionCycle: [uiGridConstants.ASC] //use uiGridConstants.DESC for pinning to the bottom
}
Row entities which have pinned = true rise to the top, even when other sorting are applied.
DISCLAIMER: I know it's not exactly answers the question, but this is how I solved it for now until I'll have a better solution:
Create an other grid above the main grid :
<div style="height:30px" ui-grid="totalGridOptions"></div>
<div ui-grid="gridOptions" class="grid"></div>
with definitions:
$scope.totalGridOptions = {showHeader:false,enableHorizontalScrollbar:false};
and then bind the columns of the main grid to the total grid (for width and other adjustments):
$scope.$watch('gridOptions', function (newVal) {
$scope.totalGridOptions.columnDefs = newVal.columnDefs;
}, true);
I think you should use something like this
$scope.gridOptions.data.unshift({label:value});
unshift adds it to the top
Edit 2 / Actual Solution: The way I finally settled this issue was by using custom header cell templates. I essentially create a second header row by adding a div at the bottom of what was previously my header. Here's a simple version:
<div class="super-special-header>
<div class="header-display-name">{{col.displayName}}</div>
<div class="totals-row">{{grid.appScope.totals[col.field]}}</div>
</div>
I store my totals on the controller's $scope and can access them in that div with grid.appScope.totals[whateverThisColumnIs]. This way I can still update them dynamically, but they don't get mixed into a sort function like my previous 'solution' was aiming for.
Edit 1 / Dead-end 'solution': Just ran into a problem with my solution, if your table is long (you have to scroll to get to bottom rows), the totals row will scroll out of view. Going to leave this 'solution' here so no one else makes the same mistake!
I had this same issue but with a twist. Since I was going to need to change the default sorting algorithms for many of the columns anyway, I set my algorithm up to skip the first element in the sort. You can use the sortingAlgorithm property on any columndef that would be part of a sortable column. This is really only a solution if you have only a few sortable columns though. It becomes unmaintainable for huge tables.
I couldn't find any built-in feature for ui-grid to keep a specific row at the top of the grid when sorting is applied. But this could be done using sortingAlgorithm parameter in the columnDefs( please refer to http://ui-grid.info/docs/#!/tutorial/Tutorial:%20102%20Sorting).
I have written an algorithm which keeps the row('total' is the particular cell value in the row) at the top of the grid without applying a sorting.
var sortingAlgorithm = function (a, b, rowA, rowB, direction) {
if (direction == 'total') {
if (a == 'total') {
return 0;
}
return (a < b) ? -1 : 1;
} else {
if (a == 'total') {
return 0;
}
if (b == 'total') {
return 1;
}
}
}