SPFx: How to re-render my WebPart on section layout change - spfx

I'm trying to make my WebPart responsive to the column width in my section layout.
I get the width of the bounding rectangle by calling
const width: number = this.domElement.getBoundingClientRect().width;
My render-function looks like this:
public render(): void {
const width: number = this.domElement.getBoundingClientRect().width;
this.domElement.innerHTML = `<div>${width}</div>`;
}
When I insert my WebPart into the SharePoint workbench, the number 736 is shown.
However, if I change the layout of the section from one column to something else, the number doesn't change.
What do I need to do to trigger the render function as soon as the layout (and therefor the width) changes?

To do that you can create an event listener and in your function set the state to re-render your webpart:
private handleWindowSizeChange() {
this.setState({
size: window.innerWidth
});
}
public componentDidMount() {
window.addEventListener('resize', this.handleWindowSizeChange.bind(this));
}
public componentWillUnmount() {
window.removeEventListener('resize', this.handleWindowSizeChange.bind(this));
}

I have used the ResizeObserver api to listen for the layout change in the layout.
The only problem is that it is not supported by IE, Edge, and Safari.
public componentDidMount() {
if(window.ResizeObserver) {
this.resizeObserver = new ResizeObserver(this.handleResize);
this.resizeObserver.observe(this.domElement)
}
}
public componentWillUnmount() {
if(window.ResizeObserver) {
this.resizeObserver.disconnect()
}
}

Related

Gnome Shell Extension: Resize and position a window on creation

I am trying to write a Gnome Shell extension that resizes and positions a window when it is created. I am using the move_resize_frame method provided by the Meta.Window object to set the window's size and position, and I've also tried move and move_resize_frame. However, while the window is sized correctly, it is not being positioned correctly, and it seems that the window manager is applying some constraints on the window position. The window is not maximized.
Here is the code I am using:
let windowCreatedId, sizeChangedId;
function init() {
}
function enable() {
windowCreatedId = global.display.connect('window-created', onWindowCreated);
}
function disable() {
global.display.disconnect(windowCreatedId);
if (sizeChangedId)
global.display.disconnect(sizeChangedId);
}
function onWindowCreated(display, win) {
sizeChangedId = win.connect('size-changed', onSizeChanged);
resizeWindow(win);
}
function onSizeChanged(win) {
resizeWindow(win);
}
function resizeWindow(win) {
win.move_resize_frame(false, 20, 20, 2000, 2000);
}
What am I missing or what should I change to make this extension work properly?
Instead of the size-changed signal, you need to connect to the first-frame signal, which is triggered when the frame is first drawn on the screen.
Here's a full implementation:
const Meta = imports.gi.Meta;
let windowCreatedId;
function init() {
}
function enable() {
windowCreatedId = global.display.connect('window-created', onWindowCreated);
}
function disable() {
global.display.disconnect(windowCreatedId);
}
function onWindowCreated(display, win) {
const act = win.get_compositor_private();
const id = act.connect('first-frame', _ => {
resizeWindow(win);
act.disconnect(id);
});
}
function resizeWindow(win) {
win.move_resize_frame(false, 20, 20, 2000, 2000);
}
It uses the window's get_compositor_private() method, which feels a little iffy, but it works (and Material Shell uses it too...)

How to add and remove classes to navbar on scroll in Vue 3 Composition Api

I have been working to create graana.com clone with Vue 3 and Composition API syntax. I want add remove classes to navbar on scroll. If user scrolls downwards we should automatically add a class ("scrolled") to navbar to apply special styles and when screen is scrolled back to top ("scrolled") class is removed.
See example at https://www.graana.com/.
I want to copy their navbar behaviour using Vue 3 Composition API.
Can you help me out?
<header class="header" ref="headRef">
import { defineComponent, ref } from "vue";
import { onMounted } from "#vue/runtime-core";
setup(){
const headRef = ref(null); // obtain the reference
onMounted(() => {
window.addEventListener("scroll", () => {
var curr = window.pageYOffset;
// You can style header-bg for style purpose
if (curr >= 100) {
headRef.value.classList.add("header-bg");
}else{
headRef.value.classList.remove("header-bg");
}
});
});
return {
headRef
}
}

Create ContentPage template with fixed view on top

Our Xamarin.Forms app works online and offline by downloading an original database to the cell phone and then syncing the SQLite database with the online database.
Our users need a way to see if they are online and if the changes they made got uploaded to the online database. What I try to achieve is to show the sync status at the top of every ContentPage, so the users can see this information all the time while working with the app.
What I tried is this: create a class "SyncInfoContentPage" that inherits from ContentPage. All ContentPages I already wrote will now not inherit from ContentPage anymore but from SyncInfoContentPage.
The SyncInfoContentPage automatically takes its Content and replaces it with a new Stacklayout that includes the SyncInfo and the original content. By doing this I don't have to rewrite the 77 ContentPages we already have.
This code works fine on Android, but on iOS the SyncInfo is not visible and (even worse) my ContentPages that inherit from SyncInfoContentPage do not react to anything anymore.
Here is my code:
public class SyncInfoContentPage : ContentPage
{
private readonly Frame SyncInfo;
public SyncInfoContentPage()
{
SyncInfo = BuildSyncInfo(); //Creates the frame with the sync Information
PropertyChanged += SyncInfoContentPage_PropertyChanged;
}
private void SyncInfoContentPage_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
// Add the SyncInfo Frame on top of the Content when the Content gets changed
if (e.PropertyName.Equals("Content"))
{
bool change = false;
// If the content already is a StackLayout, check if the SyncInfo already got added, so that theres no infinite loop.
if (Content is StackLayout)
{
var check = Content as StackLayout;
if (!check.Children.Contains(SyncInfo))
{
change = true;
}
}
else // if the Content is no StackLayout, the SyncInfo Frame can't be inside the Content yet
{
change = true;
}
if (change)
{
var layout = Content; // This is a reference, probably the error?
Device.BeginInvokeOnMainThread(() =>
{
Content = new StackLayout
{
Children = { SyncInfo, layout }
};
});
}
}
}
}
The problem is probably that iOS doesn't like this part:
var layout = Content;
Content = new StackLayout { Children = { SyncInfo, layout } };
Thanks in advance for your help and any suggestions :-)
I got it to work. The solution is simple but strange. You have to add the original Content after you added the SyncInfo status bar.
var layoutOld = Content;
var layoutNew = new StackLayout
{
Children = { SyncInfo }
};
Content = layoutNew;
layoutNeu.Children.Add(layoutOld);

Making SVG Responsive in React

I am working on a responsive utility component, to make a few D3 components responsive in react. However I deep SVG knowledge escapes me. I have based my responsive utility on this issue on github. However it isn't quite working, All it does is render the a chart, but not at the width or height passed in but rather at a really small width and height. It also doesn't resize.
import React from 'react';
class Responsive extends React.Component{
constructor () {
super();
this.state = {
size: {
w: 0,
h: 0
}
}
}
componentDidMount () {
window.addEventListener('resize', this.fitToParentSize.bind(this));
this.fitToParentSize();
}
componentWillReceiveProps () {
this.fitToParentSize();
}
componentWillUnmount() {
window.removeEventListener('resize', this.fitToParentSize.bind(this));
}
fitToParentSize () {
let elem = this.findDOMNode(this);
let w = elem.parentNode.offsetWidth;
let h = elem.parentNode.offsetHeight;
let currentSize = this.state.size;
if (w !== currentSize.w || h !== currentSize.h) {
this.setState({
size: {
w: w,
h: h
}
});
}
}
render () {
let {width, height} = this.props;
width = this.state.size.w || 100;
height = this.state.size.h || 100;
var Charts = React.cloneElement(this.props.children, { width, height});
return Charts;
}
};
export default Responsive;
Responsive width={400} height={500}>
<XYAxis data={data3Check}
xDataKey='x'
yDataKey='y'
grid={true}
gridLines={'solid'}>
<AreaChart dataKey='a'/>
<LineChart dataKey='l' pointColor="#ffc952" pointBorderColor='#34314c'/>
</XYAxis>
</Responsive>
disclaimer: I'm the author of vx a low-level react+d3 library full of visualization components.
You could use #vx/responsive or create your own higher-order component based on withParentSize() or withWindowSize() depending on what sizing you want to respond to (I've found most situations require withParentSize()).
The gist is you create a higher-order component that takes in your chart component and it attaches/removes event listeners for when the window resizes with a debounce time of 300ms by default (you can override this with a prop) and stores the dimensions in its state. The new parent dimensions will get passed in as props to your chart as parentWidth, parentHeight or screenWidth, screenHeight and you can set your svg's width and height attributes from there or calculate your chart dimensions based on those values.
Usage:
// MyChart.js
import { withParentSize } from '#vx/responsive';
function MyChart({ parentWidth, parentHeight }) {
return (
<svg width={parentWidth} height={parentHeight}>
{/* stuff */}
</svg>
);
}
export default withParentSize(MyChart);

javafx close window

I have a javafx app, and I was able to spawn another window, but I can't seem to find a way to close the the window i started in.
I used this to load the second window
var design = Launcher {};
javafx.stage.Stage
{
title: "Launcher"
scene: design.getDesignScene ()
}
stage.close(), but you would need a variable that references the original stage.
The way it worked for me:
I have the Main.fx where I instante the window I want to see first.
ex:
var mainWind:MainWindow = MainWindow{ };
MainWindow.fx will extend CustomNode and override create() method.
In the create() method I have the stage
ex:
public class MainWindow extends CustomNode{
...
var stage:Stage;
override function create():Node {
var n:Node;
stage = Stage {
width: 300
height: 180
title: "Login"
scene: Scene {
content:[ userText, userTextBox, passwdText, passwdTextBox, btnsBox ]
}
}
return n;
}
}
In MainWindow.fx I have a button with an event where I close this stage and show the other one .
ex:
Button {
text: "Login"
font:Font{ name:"Arial" size: 12}
layoutInfo:LayoutInfo {
width: loginbtn_width
}
blocksMouse: false
//close main window
onMouseClicked: function(e:MouseEvent):Void { stage.close(); }
//open the other window
action: function(){
// the ConnectionSettings will also extend CustomNode and will have its own stage
var conSetWind:ConnectionSettings = ConnectionSettings{ };
}
}
Iulia

Resources