Compare commits

..

No commits in common. "69ef122f9c0184c592ec021764d0148ddbea54ad" and "c8790f1005b481107e86bda1e922127a3f0ed290" have entirely different histories.

8 changed files with 3862 additions and 560 deletions

View File

@ -12,7 +12,6 @@ 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'));
@ -55,7 +54,6 @@ 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,10 +19,6 @@ 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();
@ -42,7 +38,6 @@ const BarraNav = ({esAdministrador, setEsAdministrador}) => {
<SeccióPobles /> <SeccióPobles />
<SeccióNecessitats /> <SeccióNecessitats />
<SeccióTipus /> <SeccióTipus />
{ esAdministrador && <SeccióCodis />}
</PanellSeccions>; </PanellSeccions>;
}; };

View File

@ -34,21 +34,15 @@ 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,7 +10,6 @@ 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}) => {
@ -32,10 +31,6 @@ 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();
@ -85,26 +80,21 @@ 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`,

View File

@ -1,252 +0,0 @@
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, setUsrSeleccionat}) => { const U = ({u, esAdministrador}) => {
const [adm, setAdm] = useState(false); const [adm, setAdm] = useState(false);
const [mon, setMon] = useState(false); const [mon, setMon] = useState(false);
@ -99,7 +99,6 @@ const U = ({u, esAdministrador, setUsrSeleccionat}) => {
return <Suspense fallback="Carregant..."> return <Suspense fallback="Carregant...">
<div <div
key={`u_${u._id}`}
style={{ style={{
// color: `lightblue`, // color: `lightblue`,
@ -191,10 +190,9 @@ const U = ({u, esAdministrador, setUsrSeleccionat}) => {
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>
@ -212,7 +210,7 @@ const U = ({u, esAdministrador, setUsrSeleccionat}) => {
// zIndex: `200` // zIndex: `200`
// }} // }}
// // onMouseLeave={ev => { // // onMouseLeave={ev => {
// // setMostraMenu(false);p // // setMostraMenu(false);
// // }} // // }}
// > // >
// {/* <Link to="/c/config"> */} // {/* <Link to="/c/config"> */}
@ -247,152 +245,69 @@ const U = ({u, esAdministrador, setUsrSeleccionat}) => {
}; };
export const Usuaris = () => {
const AssignadorDeRols = ({pobles, esEditor, rols, usrSeleccionat, ambitsUSel}) => { const [usrSeleccionat, setUsrSeleccionat] = useState(null);
const [creantRol, setCreantRol] = useState(false); useSubscribe('pobles');
const [ambitGeneral, setAmbitGeneral] = useState(false); const pobles = useTracker("pobles", () => PoblesCollection.find().fetchAsync());
// const [ambitsUSel, setAmbitsUSel] = useState([]);
// let ambitsUSel; const [esEditor, setEsEditor] = useState(false);
// let ambitsUSel = []; const userId = Meteor.userId();
// 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 () => {
// const ambits = await Roles.getScopesForUserAsync(usrSeleccionat?._id); useEffect(() => {
// // console.log("ambitsUSel: ", ambitsUSel); (async () => {
// // ambitsUSel = ambits; const comprovaAdmin = await Roles.userIsInRoleAsync(userId, ["admin"]);
// // setAmbitsUSel(ambits); setEsEditor(comprovaAdmin);
// ambitsUSel = ambits; })();
// // console.log("ambitsUSel: ", ambitsUSel);
}, []);
// })(); // })();
// }, []);
return <div // const [monitors, setMonitors] = useState(false);
style={{ // const [encarregats, setEncarregats] = useState(false);
display: `block`, // const [voluntaris, setVoluntaris] = useState(false);
border: `1px solid #6666`, // const [administradors, setAdministradors] = useState(false);
padding: `.5rem`,
borderRadius: `.3em`,
margin: `1em`,
backgroundColor: `#fffa`
}}
>
<h3>Rols</h3>
<h4>Àmbits</h4> // let mon, mons, enc, encs, vol, vols;
<h5>General:</h5> // let
<ul>{ // administradors,
rols?.map(uRol => <li // monitors,
key={`rol_${uRol}`} // encarregats,
style={{ // voluntaris
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>
{/* <ul> */} // (async () => {
{ // const admins = await Roles.getUsersInRoleAsync("administrador");
ambitsUSel.map(async aus => { // // console.log("admins: ", admins);
// administradors = await admins?.fetchAsync();
// console.log("administradors: ", administradors);
console.log("usrSeleccionat: ", usrSeleccionat); // const mons = await Roles.getUsersInRoleAsync("monitor_de_poble");
console.log("ambitsUSel: ", ambitsUSel); // // console.log("mons: ", mons);
const uRol = await Roles.getRolesForUserAsync(usrSeleccionat._id, {scope: aus, onlyScoped: true}); // monitors = await mons?.fetchAsync();
return <> // console.log("monitors: ", monitors);
<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();
confirm(`Vas a retirar el Rol ${uRol} a l'usuari ${usrSeleccionat.username}. Procedir?`) && Meteor.callAsync('retiraRols', uRol, usrSeleccionat._id, aus) // const encs = await Roles.getUsersInRoleAsync("encarregat_de_tasques");
}}>&times;</button>} // // console.log("encs: ", encs);
</span> // encarregats = await encs?.fetchAsync();
} <br /> // console.log("encarregats: ", encarregats);
</>;
})
}
{/* </ul> */}
{esEditor && creantRol && <div style={{ // const vols = await Roles.getUsersInRoleAsync("voluntari_de_poble");
display: `block`, // // console.log("vols: ", vols);
border: `1px solid #6666`, // voluntaris = await vols?.fetchAsync();
padding: `.5rem`, // console.log("voluntaris: ", voluntaris);
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 />
Rol: <select name="selRol" id="selRol"> const QuadreInfo_Usuari = () => {
{
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`,
@ -433,100 +348,13 @@ const QuadreInfo_Usuari = ({usrSeleccionat, esEditor, pobles, rols, ambitsUSel})
} }
}}>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 {... {usrSeleccionat, esEditor, pobles, rols, ambitsUSel}} /> } { esEditor && <QuadreInfo_Usuari /> }
<ul style={{ <ul style={{
display: `flex`, display: `flex`,
@ -550,7 +378,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} setUsrSeleccionat={setUsrSeleccionat} /> <U u={usr} esAdministrador={usr._id === Meteor.userId() && esEditor} />
</li>) </li>)
}</ul> }</ul>
</Suspense>; </Suspense>;

View File

@ -1,46 +0,0 @@
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 Normal file

File diff suppressed because it is too large Load Diff