forked from phc/website
fiat combobox + happy utenti page uWu
This commit is contained in:
@@ -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>
|
||||
)
|
||||
}
|
||||
+49
-27
@@ -2,12 +2,28 @@ import { effect, useComputed, useSignal } from '@preact/signals'
|
||||
import Fuse from 'fuse.js'
|
||||
import { useEffect } from 'preact/hooks'
|
||||
import { ShowMore } from './Paginate'
|
||||
import { ComboBox } from './ComboBox'
|
||||
|
||||
type User = {
|
||||
uid: 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[]) {
|
||||
users.forEach(user => {
|
||||
// strip ",+" from the end of the gecos field
|
||||
@@ -21,21 +37,25 @@ function applyPatches(users: User[]) {
|
||||
users.reverse()
|
||||
}
|
||||
|
||||
const MACCHINISTI = [
|
||||
// Lista dei macchinisti attuali
|
||||
'delucreziis',
|
||||
'minnocci',
|
||||
'baldino',
|
||||
'mossa',
|
||||
'manicastri',
|
||||
'llombardo',
|
||||
]
|
||||
const MACCHINISTI = ['delucreziis', 'minnocci', 'baldino', 'manicastri', 'llombardo', 'serdyuk']
|
||||
|
||||
const RAPPSTUD = [
|
||||
// Lista dei rappresentanti degli studenti attuali
|
||||
'smannella',
|
||||
'falcionella',
|
||||
'piazza',
|
||||
'lotti',
|
||||
'rotolo',
|
||||
'saccani',
|
||||
'carbone',
|
||||
'mburatti',
|
||||
'ppuddu',
|
||||
'marinari',
|
||||
'evsilvestri',
|
||||
'tateo',
|
||||
'graccione',
|
||||
'dilella',
|
||||
'rocca',
|
||||
'odetti',
|
||||
'borso',
|
||||
'numero',
|
||||
]
|
||||
|
||||
export const UtentiPage = () => {
|
||||
@@ -80,20 +100,16 @@ export const UtentiPage = () => {
|
||||
return (
|
||||
<>
|
||||
<div class="search-bar">
|
||||
<div
|
||||
class="filter-select"
|
||||
onClick={e => {
|
||||
e.currentTarget.querySelector('select')?.showPicker()
|
||||
}}
|
||||
>
|
||||
<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>
|
||||
<option value="rappstud">RappStud</option>
|
||||
</select>
|
||||
<span class="material-symbols-outlined">expand_more</span>
|
||||
</div>
|
||||
<ComboBox value={$filter.value} setValue={s => ($filter.value = s)}>
|
||||
{Object.fromEntries(
|
||||
Object.entries(FILTERS).map(([k, v]) => [
|
||||
k,
|
||||
<>
|
||||
<span class="material-symbols-outlined">{v.icon}</span> {v.label}
|
||||
</>,
|
||||
]),
|
||||
)}
|
||||
</ComboBox>
|
||||
<div class="search">
|
||||
<input
|
||||
type="text"
|
||||
@@ -110,7 +126,13 @@ export const UtentiPage = () => {
|
||||
{poissonUser => (
|
||||
<div class="search-result">
|
||||
<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 class="text">{poissonUser.gecos}</div>
|
||||
<div class="right">
|
||||
|
||||
@@ -186,3 +186,24 @@ Varie sezioni del sito utilizzano diverse palette di colori. Questa è la palett
|
||||
'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;
|
||||
}
|
||||
|
||||
.filter-select {
|
||||
/* .filter-select {
|
||||
width: 100%;
|
||||
height: 2.5rem;
|
||||
|
||||
@@ -646,5 +646,63 @@
|
||||
|
||||
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 {
|
||||
background: #0002;
|
||||
}
|
||||
|
||||
.invisible {
|
||||
opacity: 0 !important;
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"extends": "astro/tsconfigs/strict",
|
||||
"compilerOptions": {
|
||||
"strict": false,
|
||||
"strict": true,
|
||||
"jsx": "react-jsx",
|
||||
"jsxImportSource": "preact",
|
||||
"strictNullChecks": true,
|
||||
|
||||
Reference in New Issue
Block a user