forked from phc/website
Big refactor
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
|
||||
STYLES_SOURCES = $(wildcard src/styles/*.scss)
|
||||
SCRIPTS_SOURCES = $(wildcard src/*.js)
|
||||
|
||||
.PHONY: all
|
||||
all: scripts styles
|
||||
|
||||
.PHONY: styles
|
||||
styles: $(STYLES_SOURCES)
|
||||
npm run build:styles
|
||||
|
||||
.PHONY: scripts
|
||||
scripts: $(SCRIPTS_SOURCES)
|
||||
npm run build:scripts
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -rf dist/
|
||||
|
||||
.PHONY: debug
|
||||
debug:
|
||||
@echo "STYLES_SOURCES = $(STYLES_SOURCES)"
|
||||
@echo "SCRIPTS_SOURCES = $(SCRIPTS_SOURCES)"
|
||||
Generated
+1338
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "frontend",
|
||||
"version": "1.0.0",
|
||||
"description": "Package to generate frontend files for phc/website",
|
||||
"scripts": {
|
||||
"build:scripts": "rollup -c",
|
||||
"build:styles": "sass -s compressed --no-source-map src/styles/main.scss dist/main.css"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-node-resolve": "^13.3.0",
|
||||
"rollup": "^2.75.3",
|
||||
"rollup-plugin-terser": "^7.0.2",
|
||||
"sass": "^1.52.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"alpinejs": "^3.10.2",
|
||||
"fuse.js": "^6.6.2"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import { defineConfig } from 'rollup'
|
||||
import { terser } from 'rollup-plugin-terser'
|
||||
|
||||
export default defineConfig([
|
||||
{
|
||||
input: 'src/utenti.js',
|
||||
external: ['alpinejs', 'fuse.js'], // libraries to not bundle
|
||||
output: {
|
||||
file: 'dist/utenti.min.js',
|
||||
format: 'iife',
|
||||
globals: {
|
||||
// map library names to global constants
|
||||
alpinejs: 'Alpine',
|
||||
'fuse.js': 'Fuse',
|
||||
},
|
||||
},
|
||||
plugins: [terser()],
|
||||
},
|
||||
{
|
||||
input: 'src/profilo.js',
|
||||
external: ['alpinejs'],
|
||||
output: {
|
||||
file: 'dist/profilo.min.js',
|
||||
format: 'iife',
|
||||
globals: {
|
||||
alpinejs: 'Alpine',
|
||||
},
|
||||
},
|
||||
plugins: [terser()],
|
||||
},
|
||||
])
|
||||
@@ -0,0 +1,16 @@
|
||||
import Alpine from 'alpinejs'
|
||||
|
||||
Alpine.data('profilo', () => ({
|
||||
init() {
|
||||
console.log('Profilo!')
|
||||
},
|
||||
}))
|
||||
|
||||
Alpine.data('passwordForm', () => ({
|
||||
password: '',
|
||||
passwordAgain: '',
|
||||
passwordSame: true,
|
||||
onUpdate() {
|
||||
this.passwordSame = this.password === this.passwordAgain
|
||||
},
|
||||
}))
|
||||
@@ -0,0 +1,69 @@
|
||||
/* TODO: Don't use CDN and serve as static files */
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;700&display=swap');
|
||||
|
||||
:root {
|
||||
--fg-300: #777;
|
||||
--fg-400: #666;
|
||||
--fg-500: #333;
|
||||
--fg-600: #222;
|
||||
|
||||
--bg-000: #f0f0f0;
|
||||
--bg-100: #f0f0f0;
|
||||
--bg-500: #eaeaea;
|
||||
--bg-550: #ecedee;
|
||||
--bg-600: #e4e5e7;
|
||||
--bg-700: #d5d5d5;
|
||||
--bg-750: #c8c8c8;
|
||||
--bg-800: #c0c0c0;
|
||||
--bg-850: #b8b8b8;
|
||||
|
||||
--accent-300: #5cc969;
|
||||
--accent-400: #4eaa59;
|
||||
--accent-500: #278542;
|
||||
--accent-600: #2e974c;
|
||||
--accent-700: #154d24;
|
||||
--accent-800: #002d0d;
|
||||
|
||||
--ft-ss: 'Inter', sans-serif;
|
||||
--ft-ss-wt-light: 300;
|
||||
--ft-ss-wt-normal: 400;
|
||||
--ft-ss-wt-medium: 500;
|
||||
--ft-ss-wt-bold: 700;
|
||||
|
||||
--shadow-500: 0 0 16px 0 #00000018;
|
||||
|
||||
// Components
|
||||
--text-input-bg: var(--bg-000);
|
||||
--text-input-readonly-bg: var(--bg-600);
|
||||
--text-input-readonly-fg: var(--fg-300);
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
html {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
background: var(--bg-500);
|
||||
color: var(--fg-500);
|
||||
|
||||
font-family: var(--ft-ss);
|
||||
font-size: 17px;
|
||||
font-weight: var(--ft-ss-wt-normal);
|
||||
|
||||
width: 100%;
|
||||
min-height: 100%;
|
||||
|
||||
position: relative;
|
||||
}
|
||||
|
||||
@import './typography.scss';
|
||||
@@ -0,0 +1,78 @@
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4 {
|
||||
margin: 0;
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 0.5rem;
|
||||
|
||||
font-weight: var(--font-weight-light);
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.35rem;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 1.2rem;
|
||||
font-weight: var(--font-weight-bold);
|
||||
}
|
||||
|
||||
p,
|
||||
ul,
|
||||
ol,
|
||||
li {
|
||||
margin: 0;
|
||||
width: 70ch;
|
||||
max-width: 100%;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
p + p {
|
||||
padding-top: 0.5rem;
|
||||
}
|
||||
|
||||
ul,
|
||||
ol {
|
||||
padding: 0 0 0 1.5rem;
|
||||
}
|
||||
|
||||
hr {
|
||||
width: 50ch;
|
||||
height: 1px;
|
||||
margin: 0;
|
||||
|
||||
border: none;
|
||||
background-color: var(--bg-darker-2);
|
||||
}
|
||||
|
||||
pre {
|
||||
margin: 0.5rem 0;
|
||||
|
||||
background: var(--bg-lighter);
|
||||
border: 1px solid #cbcbcb;
|
||||
border-radius: 2px;
|
||||
box-shadow: 0 2px 4px 0 #00000033;
|
||||
|
||||
font-size: 90%;
|
||||
|
||||
display: flex;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
pre > code {
|
||||
display: block;
|
||||
margin: 0.25rem;
|
||||
}
|
||||
|
||||
p.center {
|
||||
text-align: center;
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
import Alpine from 'alpinejs'
|
||||
import Fuse from 'fuse.js'
|
||||
|
||||
const SHOW_MORE_INCREMENT = 15
|
||||
const FUSE_OPTIONS = {
|
||||
includeScore: true,
|
||||
keys: [
|
||||
'nome',
|
||||
'cognome',
|
||||
'tags',
|
||||
{ name: 'nomeCompleto', getFn: user => `${user.nome} ${user.cognome}` },
|
||||
],
|
||||
}
|
||||
|
||||
const SORT_MODES = {
|
||||
chronological: () => 0,
|
||||
name: (a, b) => (a.nome < b.nome ? -1 : 1),
|
||||
surname: (a, b) => (a.cognome < b.cognome ? -1 : 1),
|
||||
}
|
||||
|
||||
function getSortedUserList(original, mode) {
|
||||
return [...original].sort(SORT_MODES[mode])
|
||||
}
|
||||
|
||||
Alpine.data('utenti', () => ({
|
||||
searchField: '', // two-way binding for the search input field
|
||||
sortMode: 'chronological', // two-way binding for the sorting mode
|
||||
fetchedUsers: [], // hold complete user list
|
||||
sortedUserBuffer: [], // Yet another buffer of the user list for the sort mode
|
||||
fuse: new Fuse([], FUSE_OPTIONS), // current fuse instance, used to filter the list above
|
||||
searchResultsBuffer: [], // stores the full current search
|
||||
searchResults: [], // list to renderer on screen with a subset of the whole search results buffer
|
||||
async init() {
|
||||
// Get user list from server
|
||||
const response = await fetch('/api/utenti')
|
||||
this.fetchedUsers = await response.json()
|
||||
|
||||
// This will call the function "showMore()" when the user is near the end of the list
|
||||
new IntersectionObserver(entries => {
|
||||
entries.forEach(entry => {
|
||||
if (entry.isIntersecting) {
|
||||
console.log('Near the bottom of the page')
|
||||
this.showMore()
|
||||
}
|
||||
})
|
||||
}).observe(this.$refs.spinner)
|
||||
|
||||
// Initialize with an empty query
|
||||
this.updateSortMode()
|
||||
this.updateSearch()
|
||||
},
|
||||
showMore() {
|
||||
// setTimeout(() => {
|
||||
// Updates the final "searchResults" list with more items from the previous buffer
|
||||
const newCount = this.searchResults.length + SHOW_MORE_INCREMENT
|
||||
this.searchResults = this.searchResultsBuffer.slice(0, newCount)
|
||||
// }, 250) // For fun
|
||||
},
|
||||
setResults(list) {
|
||||
this.searchResultsBuffer = list.filter(
|
||||
entry => entry.score === undefined || entry.score <= 0.25
|
||||
)
|
||||
this.searchResults = this.searchResultsBuffer.slice(0, SHOW_MORE_INCREMENT)
|
||||
},
|
||||
updateSortMode() {
|
||||
this.sortedUserBuffer = getSortedUserList(this.fetchedUsers, this.sortMode)
|
||||
this.fuse.setCollection(this.sortedUserBuffer)
|
||||
this.updateSearch()
|
||||
},
|
||||
updateSearch() {
|
||||
console.time('search')
|
||||
if (this.searchField.trim().length === 0) {
|
||||
// Reset the result list
|
||||
this.setResults(this.sortedUserBuffer.map(user => ({ item: user })))
|
||||
} else {
|
||||
// Update the result list with the new results
|
||||
this.setResults(this.fuse.search(this.searchField))
|
||||
}
|
||||
console.timeEnd('search')
|
||||
},
|
||||
}))
|
||||
Reference in New Issue
Block a user