JWode
JWode
KPCKevin Powell - Community
Created by JWode on 3/10/2024 in #front-end
Consistent distances between two elements in a grid
No description
1 replies
KPCKevin Powell - Community
Created by JWode on 5/16/2023 in #front-end
Redux useSelector returns undefined
New to redux and just having difficulty working out how to access my state on line 52. useSelector is just returning undefined. Bit lost ngl. https://codepen.io/nwoodward/pen/vYVaQyW?editors=0011

const favouritesList = useSelector((state) => {
console.log(state) // undefined
return state.favourites;
});

const favouritesList = useSelector((state) => {
console.log(state) // undefined
return state.favourites;
});
1 replies
KPCKevin Powell - Community
Created by JWode on 5/13/2023 in #front-end
pointer-events and the thumb on sliders
ChatGPT seems convinced that the 'thumbs' on sliders aren't treated as part of the input range type, ie if you set point-events:none the thumbs would still be clickable. And that looks right, here: https://codepen.io/nwoodward/pen/wvYjeev Both thumbs move. But not in this code below, despite it being identical the thumbs can't be clicked, and I'm stumped as to why - can anyone see anything obvious?
{/* Slider */}
<div className='relative h-2 mt-8 border rounded'>
<div className='absolute top-0 left-8 right-12 h-full bg-[#17A2B8]'></div>
</div>
{/* Range-input */}
<div className='relative h-6'>
<input
type='range'
className='absolute w-full h-full -top-4 bg-transparent appearance-none pointer-events-none'
min='0'
max='10000'
// value='2500'
// step='100'
/>
<input
type='range'
className='absolute -top-4 w-full h-full bg-transparent appearance-none pointer-events-none'
min='0'
max='10000'
// value='2500'
// step='100'
/>
</div>
{/* Slider */}
<div className='relative h-2 mt-8 border rounded'>
<div className='absolute top-0 left-8 right-12 h-full bg-[#17A2B8]'></div>
</div>
{/* Range-input */}
<div className='relative h-6'>
<input
type='range'
className='absolute w-full h-full -top-4 bg-transparent appearance-none pointer-events-none'
min='0'
max='10000'
// value='2500'
// step='100'
/>
<input
type='range'
className='absolute -top-4 w-full h-full bg-transparent appearance-none pointer-events-none'
min='0'
max='10000'
// value='2500'
// step='100'
/>
</div>
3 replies
KPCKevin Powell - Community
Created by JWode on 5/11/2023 in #front-end
Little bit confused by this input width behaviour
You know how people go 'nose-blind' when there's a smell around for too long? Yeah, I think that's what's happened here... 😁 This was just me creating a small example for another separate question, when input elements fecked me off again, so... ...anyone know why the second price slider's min and max elements are overflowing their w-1/2 style, even though the other examples on the page aren't? I must just be being blind https://codepen.io/nwoodward/pen/wvYjeev?editors=1100
7 replies
KPCKevin Powell - Community
Created by JWode on 5/9/2023 in #front-end
Position: sticky
Go easy on me... I know this should be easy XD Not really used position sticky, but why tf is this not working? Does the direct parent have to scroll or something? Basically trying to get the sidebar and the sticky bar to, well, stick 🙂 https://codepen.io/nwoodward/pen/MWPEOzN
24 replies
KPCKevin Powell - Community
Created by JWode on 5/8/2023 in #front-end
Unique keys in React
Anyone know why I'm getting that warning here? I've a key for each renderedItem returned, and for the nested ItemComponent so am a bit confused. Haven't spent enough time banging my head against a React wall to spot it, and out of the 17 pages of errors React has provided me with none are helpful. Would appreciate it if someone can spot the error I can't!
export function FilterAccordion({ items, className }) {
const [expandedIndex, setExpandedIndex] = useState(0);

const handleClick = (index) => {
setExpandedIndex((currentExpandedIndex) => {
if (currentExpandedIndex === index) {
return -1;
}
return index;
});
};
const renderedItems = items.map((item, index) => {
const isExpanded = index === expandedIndex;
const ItemComponent = item.content.itemComponent;
const Icon = item.icon;

const classes = twMerge(
`group flex items-center px-3 py-2.5 rounded cursor-pointer bg-slate-100 hover:bg-teal-500 ${
isExpanded ? 'bg-teal-500 text-white' : ''
}`
);

const openIcon = (
<span className=''>
{isExpanded ? <GoChevronDown /> : <GoChevronLeft />}
</span>
);
return (
<div className='my-0.5' key={item.id}>
<div className={classes} onClick={() => handleClick(index)}>
<Icon className='mr-4 w-6 h-6 group-hover:text-white' />
<div className='text-base group-hover:text-white'>{item.name}</div>
<div className='ml-auto group-hover:text-white'>{openIcon}</div>
</div>
{isExpanded && (
<AccordionContent>
{item.content.options.map((option) => {
return <ItemComponent key={option} option={option} />;
})}
</AccordionContent>
)}
</div>
);
});

return <div className={className}>{renderedItems}</div>;
}
export function FilterAccordion({ items, className }) {
const [expandedIndex, setExpandedIndex] = useState(0);

const handleClick = (index) => {
setExpandedIndex((currentExpandedIndex) => {
if (currentExpandedIndex === index) {
return -1;
}
return index;
});
};
const renderedItems = items.map((item, index) => {
const isExpanded = index === expandedIndex;
const ItemComponent = item.content.itemComponent;
const Icon = item.icon;

const classes = twMerge(
`group flex items-center px-3 py-2.5 rounded cursor-pointer bg-slate-100 hover:bg-teal-500 ${
isExpanded ? 'bg-teal-500 text-white' : ''
}`
);

const openIcon = (
<span className=''>
{isExpanded ? <GoChevronDown /> : <GoChevronLeft />}
</span>
);
return (
<div className='my-0.5' key={item.id}>
<div className={classes} onClick={() => handleClick(index)}>
<Icon className='mr-4 w-6 h-6 group-hover:text-white' />
<div className='text-base group-hover:text-white'>{item.name}</div>
<div className='ml-auto group-hover:text-white'>{openIcon}</div>
</div>
{isExpanded && (
<AccordionContent>
{item.content.options.map((option) => {
return <ItemComponent key={option} option={option} />;
})}
</AccordionContent>
)}
</div>
);
});

return <div className={className}>{renderedItems}</div>;
}
11 replies
KPCKevin Powell - Community
Created by JWode on 4/27/2023 in #front-end
React keys
Should I just trust React that the keys I've set are unique if it doesn't complain? I can't see them anywhere in devtools, and was just a bit suspicious that it had quietly allowed a mistake of mine to slip through. Obviously I can add UUID or w/e, but was just interested in whether it's visible at all?
11 replies
KPCKevin Powell - Community
Created by JWode on 4/6/2023 in #front-end
The observer pattern and nested objects
I'm just struggling a little with the observer pattern and preventing observers from registering multiple times. I've got a model that implements an Observable interface, so registerObserver/removeObserver and updateObserver functions, and a View with a constructor that calls this.model.registerObserver(this). The problem is each view can have nested views, and those views are created with the new keyword. That means (I think) that I'm unable to run a check in the registerObserver function to make sure that the view isn't already registered :/ Am a bit stuck! For example: The Model
export abstract class Model<T> implements Observable {
observers: Observer[] = [];

registerObserver(observer: Observer): void {
this.observers.push(observer);
}
unregisterObserver(observer: Observer): void {
this.observers = this.observers.filter((obs) => obs !== observer);
}
updateObservers(): void {
this.observers.forEach((observer) => observer.update());
}
export abstract class Model<T> implements Observable {
observers: Observer[] = [];

registerObserver(observer: Observer): void {
this.observers.push(observer);
}
unregisterObserver(observer: Observer): void {
this.observers = this.observers.filter((obs) => obs !== observer);
}
updateObservers(): void {
this.observers.forEach((observer) => observer.update());
}
The View
export abstract class View<T extends Model<K>, K> implements Observer {

constructor(public parent: Element, public model: T) {
if (model) this.bindModel();
}
bindModel(): void {
this.model.registerObserver(this);
}

update() {
this.render();
}

{snip - event/region logic}

onRender(): void {}

render(): void {
{snip - templating logic}

this.onRender();
this.parent.append(templateElement.content);
}
}
export abstract class View<T extends Model<K>, K> implements Observer {

constructor(public parent: Element, public model: T) {
if (model) this.bindModel();
}
bindModel(): void {
this.model.registerObserver(this);
}

update() {
this.render();
}

{snip - event/region logic}

onRender(): void {}

render(): void {
{snip - templating logic}

this.onRender();
this.parent.append(templateElement.content);
}
}
Concrete implementation of the View above that involves nested Views (only looking at onRender) FormView (extends View):
onRender(): void {
const userShow = new UserShow(this.regions.userShow, this.model);
userShow.render();
const userForm = new UserForm(this.regions.userForm, this.model);
userForm.render();
}
onRender(): void {
const userShow = new UserShow(this.regions.userShow, this.model);
userShow.render();
const userForm = new UserForm(this.regions.userForm, this.model);
userForm.render();
}
No doubt this is basic, but I'm stuck! 😆 **After many shit ideas, chatGPT has suggested each View is assigned a random id, which I guess would work, but there must be a better solution
1 replies
KPCKevin Powell - Community
Created by JWode on 4/5/2023 in #front-end
Parent and child constructors
if I have a UserForm class:
class UserForm extends View {
// no constructor
}
class UserForm extends View {
// no constructor
}
and a View class:
class View {
constructor(parentElement){ this.parentElement = parentElement }
}
class View {
constructor(parentElement){ this.parentElement = parentElement }
}
Does anyone know why new UserForm('root') causes the UserForm class to inherit the View constructor and set the parentElement variable correctly? Definitely wasn't the behavior I was expecting 🤔 My best guess is that behind the scenes UserForm is creating a new object from the View's prototype, and because one isn't present in the UserForm class it inherits it rather than overriding it if it had been explicitly provided - that would also explain why I don't have to call super in this case to set View properties? This is all a guess though, it's been a while since I've looked at prototype inheritance, and this seems like a bit of an edge case that's not included often
7 replies
KPCKevin Powell - Community
Created by JWode on 3/21/2023 in #os-and-tools
Ubuntu upgrades / unattended upgrades
Apologies if this is a bit off topic for this channel - if anyone knows a good linux discord server I'll happily ask there! 🙂 If not though: - I've started serving sites from ubuntu on digital ocean droplets. I'm using unattended-upgrades, and it seems to be running:
● apt-daily-upgrade.timer - Daily apt upgrade and clean activities
Loaded: loaded (/lib/systemd/system/apt-daily-upgrade.timer; enabled; vendor preset: enabled)
Active: active (waiting) since Tue 2023-03-21 18:28:14 UTC; 16min ago
Trigger: Wed 2023-03-22 06:12:51 UTC; 11h left
Triggers: ● apt-daily-upgrade.service
● apt-daily-upgrade.timer - Daily apt upgrade and clean activities
Loaded: loaded (/lib/systemd/system/apt-daily-upgrade.timer; enabled; vendor preset: enabled)
Active: active (waiting) since Tue 2023-03-21 18:28:14 UTC; 16min ago
Trigger: Wed 2023-03-22 06:12:51 UTC; 11h left
Triggers: ● apt-daily-upgrade.service
and it has upgraded something today (I think): -rw-r--r-- 1 root root 0 Mar 21 06:14 unattended-upgrades-stamp but logging into the server today shows:
28 updates can be applied immediately.
13 of these updates are standard security updates.
28 updates can be applied immediately.
13 of these updates are standard security updates.
I'm really new to linux, especially from the cli, but from reading, shouldn't those security updates be handled? The lines in /etc/apt/apt.conf.d/50unattended-upgrades under Unattended-Upgrade::Allowed-Origins with -security in them are uncommented 🤷‍♂️ I feel a bit overwhelmed/worried about messing this up, given that I too frequently mess up my development environment with upgrades 😆 As for the other non-security updates, should I just cross my fingers and run sudo apt upgrade? Sorry if this is a bit rambling, sums up my knowledge on this!
21 replies
KPCKevin Powell - Community
Created by JWode on 3/20/2023 in #front-end
Scroll-margin-top: Why overflow hidden has an impact
So I've worked out that removing overflow: hidden fixes the problem on at least one of my sections that were ignoring the scroll-margin-top property On at least one other it has no impact. I was just wondering if anyone had any ideas why that might be? Does it scroll to the contained elements that have overflowed and aren't visible?
1 replies
KPCKevin Powell - Community
Created by JWode on 3/15/2023 in #front-end
<picture> tag and resolving images in my hero section
I'm trying to increase the performance of my site, so I've got 3 different jpg hero image sizes, and 3 different webp sizes:
<picture>
<source
type="image/webp"
srcset="${serversSmallWebp} 640w, ${serversMediumWebp} 1280w, ${serversLargeWebp} 1920w"
sizes="(min-width: ${screens.lg}) 50vw"
>
<source
type="image/jpg"
srcset="${serversSmall} 640w, ${serversMedium} 1280w, ${serversLarge} 1920w"
sizes="(min-width: ${screens.lg}) 50vw"
>
<img src=${serversSmall} alt="A close up of server racks"
class="hero__image absolute right-0 top-0 opacity-[0.6] h-full object-cover lg:h-full grayscale"
>
</picture>
<picture>
<source
type="image/webp"
srcset="${serversSmallWebp} 640w, ${serversMediumWebp} 1280w, ${serversLargeWebp} 1920w"
sizes="(min-width: ${screens.lg}) 50vw"
>
<source
type="image/jpg"
srcset="${serversSmall} 640w, ${serversMedium} 1280w, ${serversLarge} 1920w"
sizes="(min-width: ${screens.lg}) 50vw"
>
<img src=${serversSmall} alt="A close up of server racks"
class="hero__image absolute right-0 top-0 opacity-[0.6] h-full object-cover lg:h-full grayscale"
>
</picture>
If I'm importing those 6 images at the top of my module (I'm using webpack) does that have an impact on browser performance? I know that it should only serve the image that matches the supported source type, but I just wanted to make sure I wasn't unwittingly making a mistake, as I'm pretty new to using the picture element 🙂
14 replies
KPCKevin Powell - Community
Created by JWode on 3/13/2023 in #back-end
DKIM and SPF?
Obviously I can google and pick a link, but I was wondering if anyone has a decent resource they recommend for this? I'm terrible with email shit, but google's moaning about it. ----- More detail: So my server is hosted on Digital Ocean, and my email is sent through office365. I moved the DNS from GoDaddy to DO, because, well, f**k GoDaddy. From what I've read so far, I need to add the external IP of office365's mail servers to my TXT record? Does that sound right? Edit: So this is my effort so far: v=spf1 include:spf.protection.outlook.com -all based on the information in this link: https://learn.microsoft.com/en-us/microsoft-365/security/office-365-security/email-authentication-spf-configure?view=o365-worldwide I've added it as the value to my hostname (@) God only knows if this has worked
1 replies
KPCKevin Powell - Community
Created by JWode on 3/13/2023 in #front-end
Dynamically requiring images?
I've a module that contains a class that represents the Hero section of my site. The Hero has a slider of images. At the top of the class I'm importing the images:
const servers = require('...');
const solar = require('...');
const datacenter = require('...');
const servers = require('...');
const solar = require('...');
const datacenter = require('...');
Snippet of the string of html produced to insert into the DOM:
<div class="image-wrapper">
<img class="hero__image" src=${servers} alt="">
<img class="hero__image" src=${solar} alt="">
<img class="hero__image" src=${datacenter} alt="">
</div>
<div class="image-wrapper">
<img class="hero__image" src=${servers} alt="">
<img class="hero__image" src=${solar} alt="">
<img class="hero__image" src=${datacenter} alt="">
</div>
Nothing special. But now I'd like to wait until the page is fully loaded before requiring the solar and datacenter images. So in the class I add a function to set up the lazy loading:
_initLazyLoading() {
window.addEventListener('load', () => {
this.solar = require('...');
this.datacenter = require('...');
});
}
_initLazyLoading() {
window.addEventListener('load', () => {
this.solar = require('...');
this.datacenter = require('...');
});
}
But I assume this isn't working because I can't simply expect the already rendered html to update the src of the img? Not entirely sure how to approach this - especially if I then look to use a <picture> element with different sources 🤔 EDIT So the solution was to dynamically require the image in the <img> src attribute, and insert the html on load instead. I guess that also gives me some options RE the <picture> tag
1 replies
KPCKevin Powell - Community
Created by JWode on 3/12/2023 in #front-end
PUT requests and data consistency?
10 replies
KPCKevin Powell - Community
Created by JWode on 3/6/2023 in #front-end
Opinions on this gsap parallax?
I've made this parallax section with gsap and it's just a bit stuttery/has issues with continuing to scroll as the image gets higher. I've pretty much topped out in terms of skill on this one I think, but I'm still not really happy with the results and was hoping for a bit of input 🙂 https://codepen.io/nwoodward/pen/zYJdKox?editors=1011
79 replies
KPCKevin Powell - Community
Created by JWode on 3/2/2023 in #front-end
Click MouseEvent not bubbling
Maybe it's not bubbling? Maybe it is? I'm not 100% sure, but it doesn't look like it and I can't seem to work out why 🤷‍♂️
const clickEvent = new MouseEvent("click", {
"bubbles": true,
});
currentCard.dispatchEvent(clickEvent);
const clickEvent = new MouseEvent("click", {
"bubbles": true,
});
currentCard.dispatchEvent(clickEvent);
Not being handled by: document.addEventListener("click", doFlip); On line 39: https://codepen.io/nwoodward/pen/gOdWbxZ?editors=1111
4 replies
KPCKevin Powell - Community
Created by JWode on 2/27/2023 in #back-end
My life has been blocked by CORS policy: Response to preflight request doesn't pass access
Who doesn't like a bit of monday morning cors? 😆 Was hoping that someone could help me narrow down my issue if they've got a sec. I've a FE and BE server, FE using axios, BE express. Locally, the FE is on port 4600, backend 3550, and there doesn't seem to be an issue. I'd thought that was because I'd set the correct headers on the server:
module.exports = (app) => {
app.use((req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:4600');
res.setHeader('Access-Control-Allow-Credentials', 'true');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE');
res.setHeader('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Authorization, Accept');
res.setHeader('Access-Control-Expose-Headers', 'Content-Disposition');
next();
})
};
module.exports = (app) => {
app.use((req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:4600');
res.setHeader('Access-Control-Allow-Credentials', 'true');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE');
res.setHeader('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Authorization, Accept');
res.setHeader('Access-Control-Expose-Headers', 'Content-Disposition');
next();
})
};
Changing the allowed origin to something incorrect, say 4700, results in the error I'd expect: The 'Access-Control-Allow-Origin' header has a value 'http://localhost:4700' that is not equal to the supplied origin. So all of the above is working as far as I can tell. The problem comes when I upload the sites: FE: example.com, BE: api.example.com. Both are accessible, so for example, navigating to api.example.com/email will return json from a test route. However, the request that had previously worked locally now returns
Access to XMLHttpRequest at 'https://api.example.com/email/' from origin 'https://example.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Access to XMLHttpRequest at 'https://api.example.com/email/' from origin 'https://example.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
What I guess I don't understand is why locally the header is obviously present and being used (which is why it denied access when the port was changed to 4700), but when live it complains it isn't. I was hoping someone had some debugging pointers please! 🙂
31 replies
KPCKevin Powell - Community
Created by JWode on 2/22/2023 in #front-end
Can somebody help me understand these docs please?
So I think I've discovered that my problems with a 3rd party captcha service - Turnstile - are down to a race condition, and so I'm trying to explicitly render the challenge element once I know the page has loaded. Initially the docs (https://developers.cloudflare.com/turnstile/get-started/client-side-rendering/#explicitly-render-the-turnstile-widget) tell me to insert the async js script tag: <script src="https://challenges.cloudflare.com/turnstile/v0/api.js?onload=onloadTurnstileCallback" async defer></script> It then says I will:
have access to a global function with multiple callback options you can customize. For the following function to work properly, the page must contain an HTML element with ID example-container. The challenge can be invoked explicitly with the following JavaScript snippet:
window.onloadTurnstileCallback = function () {
turnstile.render('#example-container', {
sitekey: '<YOUR_SITE_KEY>',
callback: function(token) {
console.log(`Challenge Success ${token}`);
},
});
};
window.onloadTurnstileCallback = function () {
turnstile.render('#example-container', {
sitekey: '<YOUR_SITE_KEY>',
callback: function(token) {
console.log(`Challenge Success ${token}`);
},
});
};
So this is effectively creating a function that will call turnstile.render() when all JS has loaded, right? **EDIT: The problem is that onloadTurnstileCallback doesn't seem to be called every time
2 replies
KPCKevin Powell - Community
Created by JWode on 2/21/2023 in #front-end
Basic Turnstile question
If anyone has used it before, have you come across it intermittently rendering?
1 replies