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 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
-1
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user