From b6f364e0165d85a6188554cba06cfe7d1d37595c Mon Sep 17 00:00:00 2001 From: harishsehlangia Date: Thu, 5 Feb 2026 17:14:48 +0530 Subject: [PATCH 1/3] fix(password-generator): improve copy button UX and prevent layout shift --- .../password-generator/PasswordGenerator.jsx | 16 +++++++++++++--- .../password-generator-style.css | 14 ++++++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/plays/password-generator/PasswordGenerator.jsx b/src/plays/password-generator/PasswordGenerator.jsx index 935f227cfe..6151679d32 100644 --- a/src/plays/password-generator/PasswordGenerator.jsx +++ b/src/plays/password-generator/PasswordGenerator.jsx @@ -72,7 +72,11 @@ function PasswordGenerator(props) { const onCopyClick = (e) => { e.preventDefault(); navigator.clipboard.writeText(password.password); - setPassword({ ...password, status: true }); + setPassword((prev) => ({ ...prev, status: true })); + + setTimeout(() => { + setPassword((prev) => ({ ...prev, status: false })); + }, 1500); }; const ErrorBox = () => { @@ -109,8 +113,14 @@ function PasswordGenerator(props) {

Password Generator

{error && }
- -
diff --git a/src/plays/password-generator/password-generator-style.css b/src/plays/password-generator/password-generator-style.css index 4e2ae6e4e2..6c3af835fc 100644 --- a/src/plays/password-generator/password-generator-style.css +++ b/src/plays/password-generator/password-generator-style.css @@ -48,6 +48,20 @@ padding: 0.6em 1em; border-radius: 0.7em; text-transform: uppercase; + min-width: 100px; + text-align: center; +} + +.password-generator .main .inputfield .text.copied { + background-color: #b3daff; + border-radius: 0.4em; + transition: background-color 0.3s ease; +} + +.password-generator .main .inputfield .copybtn:disabled { + opacity: 0.55; + background-color: #18c0f4; + cursor: not-allowed; } .password-generator .main .block { From e29611be08b7487c1004a8110e661e023426341e Mon Sep 17 00:00:00 2001 From: harishsehlangia Date: Sun, 8 Feb 2026 17:03:04 +0530 Subject: [PATCH 2/3] fix(header): improve navbar scroll visibility logic --- .env.development | 2 +- .eslintrc.js | 176 +++++++++++++++++------------------ src/common/header/Header.jsx | 32 ++++++- src/common/header/header.css | 14 ++- src/common/home/Home.jsx | 2 +- 5 files changed, 132 insertions(+), 94 deletions(-) diff --git a/.env.development b/.env.development index 77ac6a42c4..d8b99e07d2 100644 --- a/.env.development +++ b/.env.development @@ -7,7 +7,7 @@ REACT_APP_NHOST_VERSION=v1 REACT_APP_NHOST_ENDPOINT=graphql REACT_APP_PLAY_WEB_SVC=https://api.reactplay.io/.netlify/functions/server DISABLE_ESLINT_PLUGIN=true -REACT_APP_ACTIVITIES_ON=true +REACT_APP_ACTIVITIES_ON=false REACT_APP_ACTIVITY_ID=hackrplay REACT_APP_DADJOKES_URL=https://jokeapi-v2.p.rapidapi.com/joke/ REACT_APP_DADJOKES_APIKEY='b71df95c75msha446fab91d0e935p1d0262jsn1d938cb85502' diff --git a/.eslintrc.js b/.eslintrc.js index 58ab465afb..a06b18ca14 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,88 +1,88 @@ -module.exports = { - env: { - browser: true, - node: true, - es2021: true, - jest: true - }, - extends: [ - 'eslint:recommended', - 'plugin:@typescript-eslint/recommended', - 'plugin:prettier/recommended' - ], - parser: '@typescript-eslint/parser', - parserOptions: { - ecmaFeatures: { - jsx: true - }, - ecmaVersion: 'latest', - sourceType: 'module' - }, - plugins: ['react-hooks', 'react', '@typescript-eslint', 'prettier'], - ignorePatterns: ['**/plays/index.js'], - - rules: { - // Note: you must disable the base rule as it can report incorrect errors - "no-unused-vars": "off", - "@typescript-eslint/no-unused-vars": "warn", - - 'import/extensions': 0, - - 'import/no-named-as-default-member': 0, - 'react/prop-types': 0, - 'react/display-name': 0, - 'react/react-in-jsx-scope': 0, - - // type - any would be very handy in few cases. - // The validation shouldn't be at linters end rather at code review end - '@typescript-eslint/no-explicit-any': 0, - - // Empty functions are handy for creating protocol not execution - '@typescript-eslint/no-empty-function': 0, - - // Require a whitespace at the beginning of a comment - 'spaced-comment': ['error', 'always'], - - // Maximum line length for comments - // Trailing comments allowed beyond maximum line length - 'max-len': ['error', { code: 400, comments: 200, ignoreTrailingComments: true }], - - // Require PascalCase for user-defined JSX components - 'react/jsx-pascal-case': ['error'], - - // Console logs cannot be committed. - 'no-console': ['error', { allow: ['warn', 'error'] }], - - // Require props to be sorted - 'react/jsx-sort-props': ['error', { callbacksLast: true, shorthandFirst: true }], - - // Omit boolean prop value when set to true - 'react/jsx-boolean-value': ['error', 'never'], - - // Require self closing tags in JSX/HTML - 'react/self-closing-comp': ['error', { component: true, html: true }], - - // Disallow unnecessary curly braces in JSX - 'react/jsx-curly-brace-presence': ['error', 'never'], - - // Require blank line before certain statements - 'padding-line-between-statements': [ - 'error', - { blankLine: 'always', prev: '*', next: 'function' }, - { blankLine: 'always', prev: '*', next: 'class' }, - { blankLine: 'always', prev: '*', next: 'export' }, - // Ignore consecutive export statements - { blankLine: 'any', prev: 'export', next: 'export' }, - { blankLine: 'always', prev: '*', next: 'return' }, - { blankLine: 'always', prev: '*', next: 'break' }, - { blankLine: 'always', prev: '*', next: 'continue' }, - { blankLine: 'always', prev: '*', next: 'throw' } - ], - 'prettier/prettier': ['error', { endOfLine: 'auto' }] - }, - settings: { - react: { - version: 'detect' - } - } -}; +module.exports = { + env: { + browser: true, + node: true, + es2021: true, + jest: true + }, + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:prettier/recommended' + ], + parser: '@typescript-eslint/parser', + parserOptions: { + ecmaFeatures: { + jsx: true + }, + ecmaVersion: 'latest', + sourceType: 'module' + }, + plugins: ['react-hooks', 'react', '@typescript-eslint', 'prettier'], + ignorePatterns: ['**/plays/index.js'], + + rules: { + // Note: you must disable the base rule as it can report incorrect errors + 'no-unused-vars': 'off', + '@typescript-eslint/no-unused-vars': 'warn', + + 'import/extensions': 0, + + 'import/no-named-as-default-member': 0, + 'react/prop-types': 0, + 'react/display-name': 0, + 'react/react-in-jsx-scope': 0, + + // type - any would be very handy in few cases. + // The validation shouldn't be at linters end rather at code review end + '@typescript-eslint/no-explicit-any': 0, + + // Empty functions are handy for creating protocol not execution + '@typescript-eslint/no-empty-function': 0, + + // Require a whitespace at the beginning of a comment + 'spaced-comment': ['error', 'always'], + + // Maximum line length for comments + // Trailing comments allowed beyond maximum line length + 'max-len': ['error', { code: 400, comments: 200, ignoreTrailingComments: true }], + + // Require PascalCase for user-defined JSX components + 'react/jsx-pascal-case': ['error'], + + // Console logs cannot be committed. + 'no-console': ['error', { allow: ['warn', 'error'] }], + + // Require props to be sorted + 'react/jsx-sort-props': ['error', { callbacksLast: true, shorthandFirst: true }], + + // Omit boolean prop value when set to true + 'react/jsx-boolean-value': ['error', 'never'], + + // Require self closing tags in JSX/HTML + 'react/self-closing-comp': ['error', { component: true, html: true }], + + // Disallow unnecessary curly braces in JSX + 'react/jsx-curly-brace-presence': ['error', 'never'], + + // Require blank line before certain statements + 'padding-line-between-statements': [ + 'error', + { blankLine: 'always', prev: '*', next: 'function' }, + { blankLine: 'always', prev: '*', next: 'class' }, + { blankLine: 'always', prev: '*', next: 'export' }, + // Ignore consecutive export statements + { blankLine: 'any', prev: 'export', next: 'export' }, + { blankLine: 'always', prev: '*', next: 'return' }, + { blankLine: 'always', prev: '*', next: 'break' }, + { blankLine: 'always', prev: '*', next: 'continue' }, + { blankLine: 'always', prev: '*', next: 'throw' } + ], + 'prettier/prettier': ['error', { endOfLine: 'auto' }] + }, + settings: { + react: { + version: 'detect' + } + } +}; diff --git a/src/common/header/Header.jsx b/src/common/header/Header.jsx index 4e0a962c0a..555ff6a197 100644 --- a/src/common/header/Header.jsx +++ b/src/common/header/Header.jsx @@ -1,5 +1,5 @@ import HeaderNav from './HeaderNav'; -import { useEffect, useState } from 'react'; +import { useEffect, useState, useRef } from 'react'; import { Link, useLocation } from 'react-router-dom'; import Countdown from 'react-countdown'; import './header.css'; @@ -10,6 +10,34 @@ const Header = () => { const pathName = location.pathname; const [reset, setReset] = useState({ search: false, filter: false }); + const [visible, setVisible] = useState(true); + const lastScrollY = useRef(0); + + useEffect(() => { + const handleScroll = () => { + const current = window.scrollY; + + const hero = document.getElementById('hero'); + const heroHeight = hero ? hero.offsetHeight : 0; + + // if inside hero → always visible + if (current < heroHeight) { + setVisible(true); + } else { + if (current > lastScrollY.current) { + setVisible(false); // down + } else { + setVisible(true); // up + } + } + + lastScrollY.current = current; + }; + + window.addEventListener('scroll', handleScroll); + + return () => window.removeEventListener('scroll', handleScroll); + }, []); useEffect(() => { if (location.state) { @@ -100,7 +128,7 @@ const Header = () => { )}
{ return (
-
+
From 04e2b840d07617ba62114c2c6bf7eff238c181ef Mon Sep 17 00:00:00 2001 From: harishsehlangia Date: Sun, 8 Feb 2026 17:41:10 +0530 Subject: [PATCH 3/3] fix(header): clean accidental file changes --- src/common/header/header.css | 73 +++++++++++++++++++++++------------- 1 file changed, 47 insertions(+), 26 deletions(-) diff --git a/src/common/header/header.css b/src/common/header/header.css index abdfd31be2..926de046c7 100644 --- a/src/common/header/header.css +++ b/src/common/header/header.css @@ -111,6 +111,51 @@ display: none; } +@media screen and (max-width: 1217px) { + .navbar-collapse { + display: block !important; + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + visibility: hidden; + pointer-events: none; + } + + .navbar-collapse:before { + content: ''; + background-color: var(--color-neutral-90); + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 0; + opacity: 0; + cursor: pointer; + transition: opacity 0.3s ease; + } + + .navbar-collapse .header-links { + transform: translateX(100%); + transition: transform 0.3s ease; + } + + .navbar-collapse.show { + visibility: visible; + pointer-events: auto; + } + + .navbar-collapse.show:before { + opacity: 60%; + } + + .navbar-collapse.show .header-links { + transform: translateX(0); + } +} + @media (min-width: 1217px) { .navbar-toggler { display: none; @@ -186,28 +231,7 @@ } @media screen and (max-width: 1217px) { - .navbar-collapse.show { - display: block; - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - } - - .navbar-collapse.show:before { - content: ''; - background-color: var(--color-neutral-90); - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 0; - opacity: 60%; - } - - .navbar-collapse.show .header-links { + .navbar-collapse .header-links { position: absolute; top: 0; right: 0; @@ -224,16 +248,13 @@ .navbar-collapse.show .header-links { align-items: stretch; + grid-gap: 0.2rem; } .navbar-collapse .menu-closer { display: block; } - .navbar-collapse.show .header-links { - grid-gap: 0.2rem; - } - .header-links > li.menu-spacer { margin: 0.4rem 0 1rem 0; }