fiat combobox + happy utenti page uWu

dev
Francesco Minnocci 1 month ago
parent de28d3ad6e
commit 9a10fe1ff0

Binary file not shown.

@ -0,0 +1,60 @@
import { createContext, type ComponentChildren, type JSX } from 'preact'
import { useState, useRef, useEffect } from 'preact/hooks'
export const ComboBox = ({
value,
setValue,
children,
}: {
value: string
setValue: (s: string) => void
children: Record<string, ComponentChildren>
}) => {
const [cloak, setCloak] = useState(true)
const [open, setOpen] = useState(true)
const comboRef = useRef<HTMLDivElement>(null)
useEffect(() => {
const handleClick = (e: MouseEvent) => {
if (comboRef.current && !comboRef.current.contains(e.target as Node)) {
setOpen(false)
}
}
document.addEventListener('mousedown', handleClick)
return () => document.removeEventListener('mousedown', handleClick)
}, [])
const [itemWidth, setItemWidth] = useState<number>(200)
useEffect(() => {
setOpen(false)
setCloak(false)
}, [])
return (
<div class="combobox" ref={comboRef} style={{ width: itemWidth + 48 + 'px' }}>
<div class="selected" onClick={() => setOpen(!open)}>
<div class="content">{children[value]}</div>
<span class="material-symbols-outlined">expand_more</span>
</div>
{open && (
<div
class={cloak ? 'dropdown invisible' : 'dropdown'}
ref={el => el && setItemWidth(el.offsetWidth)}
>
{Object.keys(children).map(key => (
<div
class="option"
onClick={() => {
setValue(key)
setOpen(false)
}}
>
{children[key]}
</div>
))}
</div>
)}
</div>
)
}

@ -2,12 +2,28 @@ import { effect, useComputed, useSignal } from '@preact/signals'
import Fuse from 'fuse.js' import Fuse from 'fuse.js'
import { useEffect } from 'preact/hooks' import { useEffect } from 'preact/hooks'
import { ShowMore } from './Paginate' import { ShowMore } from './Paginate'
import { ComboBox } from './ComboBox'
type User = { type User = {
uid: string uid: string
gecos: string gecos: string
} }
const FILTERS = {
utenti: {
icon: 'person',
label: 'Utenti',
},
macchinisti: {
icon: 'construction',
label: 'Macchinisti',
},
rappstud: {
icon: 'account_balance',
label: 'Rappresentanti',
},
}
function applyPatches(users: User[]) { function applyPatches(users: User[]) {
users.forEach(user => { users.forEach(user => {
// strip ",+" from the end of the gecos field // strip ",+" from the end of the gecos field
@ -21,21 +37,25 @@ function applyPatches(users: User[]) {
users.reverse() users.reverse()
} }
const MACCHINISTI = [ const MACCHINISTI = ['delucreziis', 'minnocci', 'baldino', 'manicastri', 'llombardo', 'serdyuk']
// Lista dei macchinisti attuali
'delucreziis',
'minnocci',
'baldino',
'mossa',
'manicastri',
'llombardo',
]
const RAPPSTUD = [ const RAPPSTUD = [
// Lista dei rappresentanti degli studenti attuali
'smannella', 'smannella',
'falcionella', 'lotti',
'piazza', 'rotolo',
'saccani',
'carbone',
'mburatti',
'ppuddu',
'marinari',
'evsilvestri',
'tateo',
'graccione',
'dilella',
'rocca',
'odetti',
'borso',
'numero',
] ]
export const UtentiPage = () => { export const UtentiPage = () => {
@ -80,20 +100,16 @@ export const UtentiPage = () => {
return ( return (
<> <>
<div class="search-bar"> <div class="search-bar">
<div <ComboBox value={$filter.value} setValue={s => ($filter.value = s)}>
class="filter-select" {Object.fromEntries(
onClick={e => { Object.entries(FILTERS).map(([k, v]) => [
e.currentTarget.querySelector('select')?.showPicker() k,
}} <>
> <span class="material-symbols-outlined">{v.icon}</span> {v.label}
<span class="material-symbols-outlined">filter_list</span> </>,
<select onChange={e => ($filter.value = e.currentTarget.value)} value={$filter.value}> ]),
<option value="utenti">Utenti</option> )}
<option value="macchinisti">Macchinisti</option> </ComboBox>
<option value="rappstud">RappStud</option>
</select>
<span class="material-symbols-outlined">expand_more</span>
</div>
<div class="search"> <div class="search">
<input <input
type="text" type="text"
@ -110,7 +126,13 @@ export const UtentiPage = () => {
{poissonUser => ( {poissonUser => (
<div class="search-result"> <div class="search-result">
<div class="icon"> <div class="icon">
<span class="material-symbols-outlined">person</span> <span class="material-symbols-outlined">
{RAPPSTUD.includes(poissonUser.uid)
? 'account_balance'
: MACCHINISTI.includes(poissonUser.uid)
? 'construction'
: 'person'}
</span>
</div> </div>
<div class="text">{poissonUser.gecos}</div> <div class="text">{poissonUser.gecos}</div>
<div class="right"> <div class="right">

@ -186,3 +186,24 @@ Varie sezioni del sito utilizzano diverse palette di colori. Questa è la palett
'var(--guide-lightest)', 'var(--guide-lightest)',
]} ]}
/> />
## Combo Box
I combo box sono un componente per fare dropdown scritto in Preact. Questo è un esempio di come funzionano.
```js
import { ComboBox } from '$lib/components/ComboBox'
const [value, setValue] = useState('option-1')
```
```jsx
<ComboBox value={value} setValue={setValue}>
{{
'option-1': <>Option 1</>
'option-2': <>Option 2</>
'option-3': <>Option 3</>
}}
</ComboBox>
```

@ -608,7 +608,7 @@
gap: 1rem; gap: 1rem;
} }
.filter-select { /* .filter-select {
width: 100%; width: 100%;
height: 2.5rem; height: 2.5rem;
@ -646,5 +646,63 @@
background: var(--filter-bg-color, #ddd); background: var(--filter-bg-color, #ddd);
} }
} */
.combobox {
width: 100%;
height: 2.5rem;
position: relative;
padding: 0 0.25rem 0 0.25rem;
@include neo-brutalist-card;
cursor: pointer;
--filter-bg-color-hover: color-mix(in srgb, var(--filter-bg-color, #ddd), #000 10%);
background: var(--filter-bg-color, #ddd);
.material-symbols-outlined {
padding: 0 0.35rem;
}
.selected {
height: 100%;
gap: 0.25rem;
display: grid;
grid-auto-flow: column;
grid-template-columns: 1fr auto;
align-items: center;
.content {
display: grid;
grid-auto-flow: column;
align-items: center;
justify-content: start;
}
}
.dropdown {
position: absolute;
z-index: 10;
top: calc(100% + 8px);
left: -3px;
@include neo-brutalist-card;
background: #fff;
.option {
height: 2rem;
padding: 0 0.5rem 0 0.25rem;
display: grid;
grid-auto-flow: column;
align-items: center;
justify-content: start;
&:hover {
background: #ddd;
}
}
}
} }
} }

@ -184,3 +184,7 @@ body {
::selection { ::selection {
background: #0002; background: #0002;
} }
.invisible {
opacity: 0 !important;
}

@ -1,7 +1,7 @@
{ {
"extends": "astro/tsconfigs/strict", "extends": "astro/tsconfigs/strict",
"compilerOptions": { "compilerOptions": {
"strict": false, "strict": true,
"jsx": "react-jsx", "jsx": "react-jsx",
"jsxImportSource": "preact", "jsxImportSource": "preact",
"strictNullChecks": true, "strictNullChecks": true,

Loading…
Cancel
Save