Compare commits

...

12 Commits

Author SHA1 Message Date
69ef122f9c WIP push 2025-01-10 10:44:13 +01:00
52c381b592 pre codis 2025-01-09 18:27:36 +01:00
f8072fd2d7 Versió async de 'ambits' en assignació de rols 2025-01-08 03:39:28 +01:00
f3b9214019 Eliminat package-lock.json 2025-01-08 03:18:48 +01:00
7c21d374ba Després d'un clot 2025-01-08 03:12:15 +01:00
8c67900952 Assignació de rol admin 2025-01-08 03:05:30 +01:00
6efa6956d7 Resolt problema amb versió síncrona 2025-01-08 01:32:40 +01:00
182c1025ac Unmessing. Abans de provar les solucions de kapa ai 2025-01-08 00:55:39 +01:00
945cfa46e3 codis_1 2025-01-07 21:58:03 +01:00
d6e1329d79 codis_0 2025-01-07 12:08:38 +01:00
1ca37ff79b Abans del RRv7 2024-12-15 16:13:37 +01:00
2b1dd38b95 Abans d'actualitzar a Router v7 2024-12-15 15:29:55 +01:00
8 changed files with 560 additions and 3862 deletions

View File

@ -12,6 +12,7 @@ import { BarraNav } from './BarraNav/BarraNav';
import { UserStat } from './BarraNav/UserStat'; import { UserStat } from './BarraNav/UserStat';
import { Usuaris } from './Usuaris'; import { Usuaris } from './Usuaris';
import { Codis } from './Codis';
const Loguejat = lazy(async () => await import('/imports/ui/Loguejat.jsx')); const Loguejat = lazy(async () => await import('/imports/ui/Loguejat.jsx'));
@ -54,6 +55,7 @@ export const App = () => {
<Route path="necessitats" element={ <Necessitats /> } /> <Route path="necessitats" element={ <Necessitats /> } />
<Route path="tipus" element={ <Tipus /> } /> <Route path="tipus" element={ <Tipus /> } />
<Route path="login" element={ <Login /> } /> <Route path="login" element={ <Login /> } />
{esAdministrador && <Route path="codis" element={ <Codis /> } />}
</Routes> </Routes>
</BrowserRouter> </BrowserRouter>
}; };

View File

@ -19,6 +19,10 @@ const SeccióUsuaris = lazy(async () => {
const module = await import('/imports/ui/BarraNav/PanellSeccions/PanellSeccions.jsx'); const module = await import('/imports/ui/BarraNav/PanellSeccions/PanellSeccions.jsx');
return ({ default: module.SeccióUsuaris }); return ({ default: module.SeccióUsuaris });
}); });
const SeccióCodis = lazy(async () => {
const module = await import('/imports/ui/BarraNav/PanellSeccions/PanellSeccions.jsx');
return ({ default: module.SeccióCodis });
});
const BarraNav = ({esAdministrador, setEsAdministrador}) => { const BarraNav = ({esAdministrador, setEsAdministrador}) => {
const userId = Meteor.userId(); const userId = Meteor.userId();
@ -38,6 +42,7 @@ const BarraNav = ({esAdministrador, setEsAdministrador}) => {
<SeccióPobles /> <SeccióPobles />
<SeccióNecessitats /> <SeccióNecessitats />
<SeccióTipus /> <SeccióTipus />
{ esAdministrador && <SeccióCodis />}
</PanellSeccions>; </PanellSeccions>;
}; };

View File

@ -34,15 +34,21 @@ export const SeccióUsuaris = () => <BotóSecció
linkto="usuaris" linkto="usuaris"
/>; />;
export const SeccióCodis = () => <BotóSecció
titol="Codis"
linkto="codis"
/>;
export const PanellSeccions = ({children}) => { export const PanellSeccions = ({children}) => {
return <><div style={{ return <><div style={{
display: `flex`, display: `flex`,
border: `1px solid #cccc`, border: `1px solid #cccc`,
padding: `.5rem`, padding: `.5rem`,
borderRadius: `.5rem`, borderRadius: `.5rem`,
backgroundColor: `lightslategray` backgroundColor: `lightslategray`,
flexWrap: `wrap`,
justifyContent: `space-around`
}}>{children}</div> }}>{children}</div>
<br /> <br />
</>; </>;
}; };

View File

@ -10,6 +10,7 @@ import { Roles } from 'meteor/roles';
// import { groupBy } from 'lodash'; // import { groupBy } from 'lodash';
//import Radium from 'radium'; //import Radium from 'radium';
import { useLongTap } from '../hooks';
const IndicadorMissatges = ({notif}) => { const IndicadorMissatges = ({notif}) => {
@ -31,6 +32,10 @@ const UserStat = ({esAdministrador, setEsAdministrador}) => {
// const [esAdministrador, setEsAdministrador] = useState(false); // const [esAdministrador, setEsAdministrador] = useState(false);
const { onPointerDown, onPointerUp, onPointerCancel, onPointerOut } = useLongTap(() => {
alert('Long tap detected!');
});
const u = useTracker("user", async () => await Meteor.userAsync()); const u = useTracker("user", async () => await Meteor.userAsync());
const userId = Meteor.userId(); const userId = Meteor.userId();
@ -80,21 +85,26 @@ const UserStat = ({esAdministrador, setEsAdministrador}) => {
fontWeight: `bold`, fontWeight: `bold`,
display: `inline-block` display: `inline-block`
}} }}
onMouseEnter={ev => { // onMouseEnter={ev => {
setMostraMenu(true); // setMostraMenu(true);
}} // }}
// title="Logout" // // title="Logout"
onClick={ev => { // onClick={ev => {
ev.stopPropagation(); // ev.stopPropagation();
ev.preventDefault(); // ev.preventDefault();
// console.log("u: ", u); // // console.log("u: ", u);
navigate(`/${u.username}`); // navigate(`/${u.username}`);
}} // }}
onPointerDown={onPointerDown}
onPointerUp={onPointerUp}
onPointerCancel={onPointerCancel}
onPointerOut={onPointerOut}
> >
{ (u && esAdministrador) && <div style={{ { (u && esAdministrador) && <div style={{
color: `red`, color: `red`,

252
imports/ui/Codis.jsx Normal file
View File

@ -0,0 +1,252 @@
import React, { Suspense, useEffect, useState, useRef, lazy } from 'react';
import { Meteor } from 'meteor/meteor';;
import { NecessitatsCollection } from '/imports/api/necessitats.js';
import { PoblesCollection } from '../api/pobles';
// import { TipusCollection } from '../api/tipus';
import { useSubscribe, useTracker, useFind } from 'meteor/react-meteor-data/suspense';
import { Roles } from 'meteor/roles';
// import { useUserId } from 'meteor/react-meteor-accounts';
import Select from 'react-select';
import CreatableSelect from 'react-select/creatable';
// import AsyncCreatableSelect from 'react-select/async-creatable';
// import { BarraNav } from "./BarraNav/BarraNav";
export const Codis = () => {
// const [permes, setPermes] = useState(false);
const [esEditor, setEsEditor] = useState(false);
const userId = Meteor.userId();
useEffect(() => {
(async () => {
const comprovaAdmin = await Roles.userIsInRoleAsync(userId, ["admin"]);
setEsEditor(comprovaAdmin);
})();
}, []);
// console.log("isAdmin: ", isAdmin) ;
const [ambitSeleccionat, setAmbitSeleccionat] = useState(null);
useSubscribe('necessitats');
const necessitats = useTracker("necessitats", () => NecessitatsCollection.find().fetchAsync());
useSubscribe('pobles');
const pobles = useTracker("pobles", () => PoblesCollection.find().fetchAsync());
const ambits = pobles?.map(p => p?.ambitAssociat);
// console.log("ambits: ", ambits);
// useSubscribe('tipus');
// const ambits = useTracker("ambits", () => Collection.find().fetchAsync());
// console.log("tipus: ", tipus);
// console.log("necessitats: ", necessitats);
// console.log("tipusSeleccionat: ", tipusSeleccionat);
// const filterAmbit = (inputValue) => {
// return ambits.filter((i) =>
// i.toLowerCase().includes(inputValue.toLowerCase())
// );
// };
// const [ambitGeneral, setAmbitGeneral] = useState(ambitSeleccionat === "GENERAL");
const SelectorDeRol = () => {
return <>
<label htmlFor="selRol">Rol: </label>
<Select
key={Math.random()}
name="selRol"
// filterOption={(opts) => necessitats.find(nec => nec.tipus === tipusSeleccionat._id)}
options={
(ambitSeleccionat?.value === "GENERAL")
? [
{
value: "admin",
label: "administrador"
},
{
value: "usuari",
label: "usuari"
}
]
: [
{
value: "monitor_de_poble",
label: "monitor_de_poble"
},
{
value: "encarregat_de_tasques",
label: "encarregat_de_tasques"
},
{
value: "voluntari_de_poble",
label: "voluntari_de_poble"
}
]
}
/>
</>;
}
const QuadreInfo_Codis = () => {
return <div style={{
display: `inline-block`,
border: `1px solid #6666`,
padding: `.5rem`,
borderRadius: `.3em`,
backgroundColor: `lightcyan`
}}>
{ambitSeleccionat && <h1>{ambitSeleccionat?.value}</h1>}
<h1>Codis</h1>
<p>Tria el rol que adquirirà qui utilitze el codi.</p>
<form
action={d => {
// if (d.get('selTipus'))
try {
Meteor.callAsync('editaOAfigNecessitat', {
...tipusSeleccionat || [],
titol: d.get('titol'),
tipus: d.get('selTipus') || "",
poble: d.get('selPoble')
})
.then(() => setAmbitSeleccionat(null))
.catch(err => console.error(err))
;
} catch (err) {
alert(err);
console.error(err);
}
}}
>
<label htmlFor="selAmbit">Àmbit: </label>
{/* Si
l'usuari és ADMINISTRADOR o té permisos de responsabilitat, podrà crear nous Tipus al CREAR o a l'EDITAR
si no
tansols podrà assignar a un tipus predefinit o al de ALTRES
*/}
<Select
name="selAmbit"
// formatCreateLabel={(inputValue) => "Crear nou tipus..."}
// filterOption={filterAmbit}
defaultValue={ambitSeleccionat}
options={
[...
ambits
.map((v,i) => ({
value: v,
label: v
}))
.sort((a,b) => a.label.toLowerCase() > b.label.toLowerCase()),
{
value: "GENERAL",
label: "GENERAL"
}
]
}
// onCreateOption={(inputValue) => Meteor.callAsync('afigT', {titol: inputValue})}
isSearchable
// loadOptions={tipus.map((v,i) => ({value: v, label: v.titol}))}
onChange={amb => {
setAmbitSeleccionat(amb);
console.log("amb: ", amb);
}}
/>
{/* <select name="selTipus">
<option value="moble">Moble</option>
<option value="maquinari">Maquinari</option>
<option value="menjar">Menjar i beguda</option>
<option value="infantil">Infantils</option>
<option value="higiene">Higiene</option>
</select> */}
<br />
<SelectorDeRol {... {ambitSeleccionat}} />
<br />
<label htmlFor="actiu">Actiu? </label>
<input type="checkbox" name='actiu' />
<br /><br />
<label htmlFor="periode_validesa">Periode de validesa: </label>
<input type="date" name="periode_validesa" />
{/* <select name="selPoble">
{
pobles.map((v,i) => <option key={`optPob${i}`}>{v.nomPoble}</option>)
}
</select> */}
<br />
<input type='submit' value="Enviar" />
{ambitSeleccionat && esEditor && <button onClick={ev => {
ev.preventDefault();
if (confirm(`Vas a eliminar el poble "${pobleSeleccionat.nomPoble}". És una operació irreversible. Procedir?`)) {
Meteor.callAsync('eliminaPoble', pobleSeleccionat._id).catch(err => console.error(err));
setNecessitatSeleccionada(null);
}
}}>Elimina</button>}
</form>
</div>;
};
return <Suspense fallback={<>Carregant...</>} >
<QuadreInfo_Codis />
<br /><br />
{/* <AsyncCreatableSelect
cacheOptions
defaultOptions={tipus.map((v,i) => ({value: v, label: v.titol})) }
loadOptions={tipus.map((v,i) => ({value: v, label: v.titol}))}
/> */}
{/* <SelectSearch options={options} value="sv" name="language" placeholder="Choose your language" /> */}
<ul style={{
display: `flex`,
flexWrap: `wrap`,
justifyContent: `space-evenly`,
rowGap: `.3em`,
alignContent: `space-around`
}}>{
ambits
.sort((a,b) => a.toLowerCase() > b.toLowerCase())
.map(amb => <li
key={amb}
style={{
display: `block`,
border: `1px solid #6666`,
borderRadius: `.4em`,
padding: `4px`,
listStyle: `none`,
backgroundColor: `${'lightgreen' || 'lightcoral'}`
}}
>
{amb}{esEditor && <button onClick={() => {setAmbitSeleccionat({value: amb, label: amb})}}>Edita</button>}
</li>)
}</ul>
</Suspense>;
};

View File

@ -36,7 +36,7 @@ import { useNavigate, Link } from 'react-router-dom';
// </span>; // </span>;
// }; // };
const U = ({u, esAdministrador}) => { const U = ({u, esAdministrador, setUsrSeleccionat}) => {
const [adm, setAdm] = useState(false); const [adm, setAdm] = useState(false);
const [mon, setMon] = useState(false); const [mon, setMon] = useState(false);
@ -99,6 +99,7 @@ const U = ({u, esAdministrador}) => {
return <Suspense fallback="Carregant..."> return <Suspense fallback="Carregant...">
<div <div
key={`u_${u._id}`}
style={{ style={{
// color: `lightblue`, // color: `lightblue`,
@ -190,9 +191,10 @@ const U = ({u, esAdministrador}) => {
ev.preventDefault(); ev.preventDefault();
ev.stopPropagation(); ev.stopPropagation();
alert("Click en missatges"); // alert("Click en missatges");
}} }}
> >
{<button onClick={() => {setUsrSeleccionat(u)}}>Edita</button>}
{/* <IndicadorMissatges notif={false} /> */} {/* <IndicadorMissatges notif={false} /> */}
</span> </span>
</div> </div>
@ -210,7 +212,7 @@ const U = ({u, esAdministrador}) => {
// zIndex: `200` // zIndex: `200`
// }} // }}
// // onMouseLeave={ev => { // // onMouseLeave={ev => {
// // setMostraMenu(false); // // setMostraMenu(false);p
// // }} // // }}
// > // >
// {/* <Link to="/c/config"> */} // {/* <Link to="/c/config"> */}
@ -245,69 +247,152 @@ const U = ({u, esAdministrador}) => {
}; };
export const Usuaris = () => {
const [usrSeleccionat, setUsrSeleccionat] = useState(null); const AssignadorDeRols = ({pobles, esEditor, rols, usrSeleccionat, ambitsUSel}) => {
useSubscribe('pobles'); const [creantRol, setCreantRol] = useState(false);
const pobles = useTracker("pobles", () => PoblesCollection.find().fetchAsync()); const [ambitGeneral, setAmbitGeneral] = useState(false);
// const [ambitsUSel, setAmbitsUSel] = useState([]);
const [esEditor, setEsEditor] = useState(false); // let ambitsUSel;
const userId = Meteor.userId(); // let ambitsUSel = [];
// useEffect(() => {
const usuaris = useTracker("usuaris", () => {
Meteor.subscribe('usuaris');
return Meteor.users.find().fetch();//.filter(u => u._id !== Meteor.userId());
});
// console.log("isAdmin: ", isAdmin) ;
console.log("usuaris: ", usuaris);
// (async () => { // (async () => {
useEffect(() => { // const ambits = await Roles.getScopesForUserAsync(usrSeleccionat?._id);
(async () => { // // console.log("ambitsUSel: ", ambitsUSel);
const comprovaAdmin = await Roles.userIsInRoleAsync(userId, ["admin"]); // // ambitsUSel = ambits;
setEsEditor(comprovaAdmin); // // setAmbitsUSel(ambits);
})(); // ambitsUSel = ambits;
// // console.log("ambitsUSel: ", ambitsUSel);
}, []);
// })(); // })();
// }, []);
// const [monitors, setMonitors] = useState(false); return <div
// const [encarregats, setEncarregats] = useState(false); style={{
// const [voluntaris, setVoluntaris] = useState(false); display: `block`,
// const [administradors, setAdministradors] = useState(false); border: `1px solid #6666`,
padding: `.5rem`,
borderRadius: `.3em`,
margin: `1em`,
backgroundColor: `#fffa`
}}
>
<h3>Rols</h3>
// let mon, mons, enc, encs, vol, vols; <h4>Àmbits</h4>
// let <h5>General:</h5>
// administradors, <ul>{
// monitors, rols?.map(uRol => <li
// encarregats, key={`rol_${uRol}`}
// voluntaris style={{
// ; display: `block`,
border: `1px solid #6666`,
borderRadius: `.4em`,
padding: `4px`,
listStyle: `none`,
backgroundColor: `${'orange'}`,
textAlign: `center`
}}
>
{uRol}
{esEditor && <button onClick={ev => {
ev.preventDefault();
ev.stopPropagation();
confirm(`Vas a retirar el Rol ${uRol} a l'usuari ${usrSeleccionat.username}. Procedir?`) && Meteor.callAsync('retiraRols', uRol, usrSeleccionat._id)
}}>&times;</button>}
</li>)
}</ul>
// (async () => { {/* <ul> */}
// const admins = await Roles.getUsersInRoleAsync("administrador"); {
// // console.log("admins: ", admins); ambitsUSel.map(async aus => {
// administradors = await admins?.fetchAsync();
// console.log("administradors: ", administradors);
// const mons = await Roles.getUsersInRoleAsync("monitor_de_poble"); console.log("usrSeleccionat: ", usrSeleccionat);
// // console.log("mons: ", mons); console.log("ambitsUSel: ", ambitsUSel);
// monitors = await mons?.fetchAsync(); const uRol = await Roles.getRolesForUserAsync(usrSeleccionat._id, {scope: aus, onlyScoped: true});
// console.log("monitors: ", monitors); return <>
<h5>{aus}:</h5>
{
<span style={{
display: `block`,
border: `1px solid #6666`,
borderRadius: `.4em`,
padding: `4px`,
listStyle: `none`,
backgroundColor: `${'orange'}`,
textAlign: `center`
}}>{uRol}
{esEditor && <button onClick={ev => {
ev.preventDefault();
ev.stopPropagation();
// const encs = await Roles.getUsersInRoleAsync("encarregat_de_tasques"); confirm(`Vas a retirar el Rol ${uRol} a l'usuari ${usrSeleccionat.username}. Procedir?`) && Meteor.callAsync('retiraRols', uRol, usrSeleccionat._id, aus)
// // console.log("encs: ", encs); }}>&times;</button>}
// encarregats = await encs?.fetchAsync(); </span>
// console.log("encarregats: ", encarregats); } <br />
</>;
})
}
{/* </ul> */}
// const vols = await Roles.getUsersInRoleAsync("voluntari_de_poble"); {esEditor && creantRol && <div style={{
// // console.log("vols: ", vols); display: `block`,
// voluntaris = await vols?.fetchAsync(); border: `1px solid #6666`,
// console.log("voluntaris: ", voluntaris); padding: `.5rem`,
// })(); borderRadius: `.3em`,
margin: `1em`,
backgroundColor: `#fffa`
}}>
<form action={d => {
if (ambitGeneral) {
Meteor.callAsync('assignaRol', usrSeleccionat._id, d.get('selRol'));
} else {
Meteor.callAsync('assignaRol', usrSeleccionat._id, d.get('selRol'), d.get('selAmbit'));
}
}}>
Àmbit: <select name="selAmbit" id="selAmbit"
onChange={ev => setAmbitGeneral(ev.target.value === "GENERAL")}
>
{
pobles.sort((a,b) => a.ambitAssociat > b.ambitAssociat).map((pob,i) => <option key={`optAmb_${i}`} value={pob.ambitAssociat}>{pob.ambitAssociat}</option>)
}
<option value={"GENERAL"}>General</option>
</select> <br />
const QuadreInfo_Usuari = () => { Rol: <select name="selRol" id="selRol">
{
ambitGeneral
? <option value={"admin"}>Administrador general</option>
: <>
<option value="monitor_de_poble">Monitor de poble</option>
<option value="encarregat_de_tasques">Encarregat de tasques</option>
<option value="voluntari_de_poble">Voluntairi de poble</option>
</>
}
</select> <br /> <br />
<input type="submit" value="Estableix nou rol" />
</form>
</div>}
{esEditor && usrSeleccionat && <button
onClick={ev => {
ev.preventDefault();
ev.stopPropagation();
return false;
}}
onPointerUp={ev => {
ev.preventDefault();
ev.stopPropagation();
setCreantRol(!creantRol);
return false;
}}
>+</button>}
</div>;
};
const QuadreInfo_Usuari = ({usrSeleccionat, esEditor, pobles, rols, ambitsUSel}) => {
return <div style={{ return <div style={{
display: `inline-block`, display: `inline-block`,
@ -348,13 +433,100 @@ export const Usuaris = () => {
} }
}}>Elimina</button> }}>Elimina</button>
</form> </form>
{ esEditor && <Suspense>
<AssignadorDeRols {... {esEditor, usrSeleccionat, rols, pobles, ambitsUSel}} />
</Suspense>
}
</div>; </div>;
}; };
export const Usuaris = () => {
const [usrSeleccionat, setUsrSeleccionat] = useState(null);
useSubscribe('pobles');
const pobles = useTracker("pobles", () => PoblesCollection.find().fetchAsync());
const [esEditor, setEsEditor] = useState(false);
const userId = Meteor.userId();
const usuaris = useTracker("usuaris", () => {
Meteor.subscribe('usuaris');
return Meteor.users.find().fetch();//.filter(u => u._id !== Meteor.userId());
});
// console.log("isAdmin: ", isAdmin) ;
console.log("usuaris: ", usuaris);
// const [ambitsUSel, setAmbitsUSel] = useState([]);
// (async () => {
// let ambitsUSel = [];
useEffect(() => {
(async () => {
const comprovaAdmin = await Roles.userIsInRoleAsync(userId, ["admin"]);
setEsEditor(comprovaAdmin);
// console.log("ambitsUSel: ", ambitsUSel);
})();
}, []);
const ambitsUSel = useTracker("ambits", async () => {
return await Roles.getScopesForUserAsync(usrSeleccionat?._id);
});
// setAmbitsUSel(ambits);
const rols = useTracker("rols", async () => {
return await Roles.getRolesForUserAsync(usrSeleccionat?._id);
});
// })();
// const [monitors, setMonitors] = useState(false);
// const [encarregats, setEncarregats] = useState(false);
// const [voluntaris, setVoluntaris] = useState(false);
// const [administradors, setAdministradors] = useState(false);
// let mon, mons, enc, encs, vol, vols;
// let
// administradors,
// monitors,
// encarregats,
// voluntaris
// ;
// (async () => {
// const admins = await Roles.getUsersInRoleAsync("administrador");
// // console.log("admins: ", admins);
// administradors = await admins?.fetchAsync();
// console.log("administradors: ", administradors);
// const mons = await Roles.getUsersInRoleAsync("monitor_de_poble");
// // console.log("mons: ", mons);
// monitors = await mons?.fetchAsync();
// console.log("monitors: ", monitors);
// const encs = await Roles.getUsersInRoleAsync("encarregat_de_tasques");
// // console.log("encs: ", encs);
// encarregats = await encs?.fetchAsync();
// console.log("encarregats: ", encarregats);
// const vols = await Roles.getUsersInRoleAsync("voluntari_de_poble");
// // console.log("vols: ", vols);
// voluntaris = await vols?.fetchAsync();
// console.log("voluntaris: ", voluntaris);
// })();
return <Suspense fallback={<>Carregant...</>} > return <Suspense fallback={<>Carregant...</>} >
<h1>Usuaris</h1> <h1>Usuaris</h1>
{ esEditor && <QuadreInfo_Usuari /> } { esEditor && <QuadreInfo_Usuari {... {usrSeleccionat, esEditor, pobles, rols, ambitsUSel}} /> }
<ul style={{ <ul style={{
display: `flex`, display: `flex`,
@ -378,7 +550,7 @@ export const Usuaris = () => {
}} }}
> >
{/* {usr.username}{esEditor && <button onClick={() => {setUsrSeleccionat(usr)}}>Edita</button>} */} {/* {usr.username}{esEditor && <button onClick={() => {setUsrSeleccionat(usr)}}>Edita</button>} */}
<U u={usr} esAdministrador={usr._id === Meteor.userId() && esEditor} /> <U u={usr} esAdministrador={usr._id === Meteor.userId() && esEditor} setUsrSeleccionat={setUsrSeleccionat} />
</li>) </li>)
}</ul> }</ul>
</Suspense>; </Suspense>;

46
imports/ui/hooks.jsx Normal file
View File

@ -0,0 +1,46 @@
import { useState, useEffect } from 'react';
const useLongTap = (onLongTap, duration = 500) => {
const [isPressing, setIsPressing] = useState(false);
const [startTime, setStartTime] = useState(null);
useEffect(() => {
if (isPressing && startTime) {
const elapsedTime = performance.now() - startTime;
if (elapsedTime >= duration) {
onLongTap();
setIsPressing(false);
setStartTime(null);
}
}
}, [isPressing, startTime, onLongTap, duration]);
const handlePointerDown = (event) => {
setIsPressing(true);
setStartTime(performance.now());
};
const handlePointerUp = () => {
setIsPressing(false);
setStartTime(null);
};
const handlePointerCancel = () => {
setIsPressing(false);
setStartTime(null);
};
const handlePointerOut = () => {
setIsPressing(false);
setStartTime(null);
};
return {
onPointerDown: handlePointerDown,
onPointerUp: handlePointerUp,
onPointerCancel: handlePointerCancel,
onPointerOut: handlePointerOut,
};
};
export { useLongTap };

3795
package-lock.json generated

File diff suppressed because it is too large Load Diff