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

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
}
}

Related

Customizing colors for PrimeReact datatable

There is a way to change a background or text color to a row in PrimeReact Datatable that is using rowClassName={rowClass} where rowClass is a function that allows returning a class configured in the CSS file.
but... what if I want to choose an arbitrary color? for example, one fetched from a database in #RRGGBB format?
Reading de documentation I can't see a way to call a function to return the style string. Another way could be, creating dynamically the class name, for example...
class RRGGBB for a selected color, define this class with background: #RRGGBB and let rowClassName={rowClass} call rowClass function returning this dynamically created class...
I have this approach, but don't work:
const mycolor = "#00ff00";
function createStyle() {
const style = document.createElement("style");
// add CSS styles
style.innerHTML = `
.lulu {
color: white;
background-color: ${mycolor};
}
`;
// append the style to the DOM in <head> section
document.head.appendChild(style);
}
createStyle();
const rowClass = (data) => {
return {
"lulu": data.category === "Accessories"
};
};
.....
<DataTable value={products} rowClassName={rowClass}>
this code is a modified version of the sample code by prime react, here, in sandbox:
https://codesandbox.io/s/o6k1n
thanks!
I have solved it...
What I did is to create a dynamic css, and then use it:
function createStyle(color) {
var style = document.getElementsByTagName("style");
var colortag = color.replace("#", "mycolor");
//Assuming there is a style section in the head
var pos = style[0].innerHTML.indexOf(colortag);
if(pos<0)
style[1].innerHTML += "."+colortag+`
{
color: ${color}!important;
}
`;
return colortag;
const rowClass = (data) => {
var colortag;
if (data.COLOR!=undefined)
colortag=createStyle(data.COLOR);
return { [colortag]: ata.COLOR!=undefined };
}
<DataTable ref={dt} value={Data} rowClassName={rowClass}>
<column>......</column>
</DataTable>
With this code, if in the data there is a field called COLOR:"#RRGGBB" then it will create a style with this color and use it as text color. The same can be applied to background or whatever

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

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()
}
}

How do I initialise a NativeScript app fully programmatically (without XML)?

Here's what I have so far. The background goes green (the colour of the Page), but I'd expect a purple ContentView with some text inside to fill the page, too.
Is there anything further I'm missing?
import { on, run, launchEvent } from "tns-core-modules/application";
import { Frame } from "tns-core-modules/ui/frame/frame";
import { ContentView } from "tns-core-modules/ui/content-view/content-view";
import { TextBase } from "tns-core-modules/ui/text-base/text-base";
import { Page } from "tns-core-modules/ui/page/page";
on(launchEvent, (data) => {
const frame = new Frame();
const page = new Page();
page.backgroundColor = "green";
const contentView = new ContentView();
const textBase = new TextBase();
contentView.height = 100;
contentView.width = 100;
contentView.backgroundColor = "purple";
textBase.text = "Hello, world!";
contentView._addView(textBase);
page.bindingContext = contentView;
frame.navigate({ create: () => page });
data.root = page; // Incidentally, should this be the frame or the page?
});
run();
You are almost on track, you just need slight modification on your code.
import { on, run, launchEvent } from 'tns-core-modules/application';
import { Frame } from 'tns-core-modules/ui/frame/frame';
import { ContentView } from 'tns-core-modules/ui/content-view/content-view';
import { TextField } from 'tns-core-modules/ui/text-field';
import { Page } from 'tns-core-modules/ui/page/page';
run({
create: () => {
const frame = new Frame();
frame.navigate({
create: () => {
const page = new Page();
page.backgroundColor = "green";
const contentView = new ContentView();
const textField = new TextField();
contentView.height = 100;
contentView.width = 100;
contentView.backgroundColor = "purple";
textField.text = "Hello, world!";
contentView.content = textField;
page.content = contentView;
return page;
}
});
return frame;
}
});
You don't have to wait for launch event, you could set the root frame in run method itself.
In your code, you were creating the frame but never adding it to root UI element or mark the frame itself as root element
It's recommended to use .content to add child for a ContentView / Page as they are originally designed to hold one child element only.
Use TextField / TextView for input text, TextBase is just a base class.
It seems to me that you try to overcomplicate. You can replace XML with code just by implementing createPage method - Create a page via code.
I just modified default NS + TypeScript Playground template to operate without XML - NS + TypeScript template without XML.
I think you can't leave run as empty as it is expecting an entry to start the app. From {NS} website,
You can use this file to perform app-level initializations, but the
primary purpose of the file is to pass control to the app's root
module. To do this, you need to call the application.run() method and
pass a NavigationEntry with the desired moduleName as the path to the
root module relative to your /app folder.
if you look for run code in "tns-core-modules/application"
function run(entry) {
createRootFrame.value = false;
start(entry);
}
exports.run = run;
and
function start(entry) {
if (started) {
throw new Error("Application is already started.");
}
started = true;
mainEntry = typeof entry === "string" ? { moduleName: entry } : entry;
if (!androidApp.nativeApp) {
var nativeApp = getNativeApplication();
androidApp.init(nativeApp);
}
}

How to properly reference modals as of NativeScript 4

I have built an app with NativeScript Angular (now on v4.1). I am using the code below to set a status bar color on Android, which works fine for "regular" views by using angular routing:
if (isAndroid) {
if (app.android && device.sdkVersion >= '21') {
const nativeColor = new Color('purple').android;
const window = app.android.startActivity.getWindow();
window.addFlags(android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
app.android.startActivity.getWindow().setStatusBarColor(nativeColor);
}
}
However, it does not work in a modal, where the colored status-bar turns to black. Anyone any ideas why this is? My best guess would be that a modal is not referenced by app.android.startActivity.getWindow() but I’m unclear as how to get it.
Not sure to what extent this is related, but also I am unable to set a different loading indicator on iOS in modals by using this code from the docs (again works fine in non-modal views):
if (isIOS) {
var indicator = this.page.getViewById("spinner");
indicator.ios.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.WhiteLarge;
}
Thanks for any pointers!
P.S.
Here is a demo project which displays the problem both with the status bar on Android, as well as not being able to set the activity indicator on iOS.
For anyone reading this later on: I had some help on the NativeScript forum and did not yet accomplish setting the statusbar color on the modal, but I was able to span the background under the status bar in the modal component which is good enough for my purposes.
I was also able to change the ActivityIndicator in the modal simply by using an Angular ViewChild on the indicator element instead of finding it through Page (which turns out does not refer to the modal).
import { Component, OnInit, AfterViewInit, ViewChild, ElementRef } from "#angular/core";
import { ModalDialogParams } from "nativescript-angular/modal-dialog";
import { isIOS, isAndroid, device } from 'platform';
import * as app from "application";
declare var UIActivityIndicatorViewStyle: any;
declare var android: any;
#Component({
template: `
<StackLayout #root>
<StackLayout orientation="horizontal">
<ActivityIndicator #spinner id="spinner" busy="true"></ActivityIndicator>
<ActivityIndicator busy="true"></ActivityIndicator>
</StackLayout>
</StackLayout>
`
})
export class ModalTest implements OnInit, AfterViewInit {
#ViewChild('root') root: ElementRef;
#ViewChild('spinner') spinner: ElementRef;
constructor(
public modalParams: ModalDialogParams
) {}
public ngOnInit() {
/* show large activity indicator on iOS */
if (isIOS) {
this.spinner.nativeElement.ios.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.WhiteLarge;
}
}
public ngAfterViewInit() {
if (isAndroid) {
setTimeout(() => {
if (app.android && device.sdkVersion >= '21') {
const dialog = this.root.nativeElement._dialogFragment.getDialog();
const window = dialog.getWindow();
window.addFlags(android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
}
},1);
}
}
public closeModal() {
this.modalParams.closeCallback();
}
}

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);

Resources