Better darkmode and chart.js start
This commit is contained in:
34
src/chart.js
Executable file
34
src/chart.js
Executable file
@@ -0,0 +1,34 @@
|
||||
import Chart from 'chart.js/auto'
|
||||
|
||||
const data = [
|
||||
{year: 2009, count: 10},
|
||||
{year: 2010, count: NaN},
|
||||
{year: 2011, count: 20},
|
||||
{year: 2012, count: 15},
|
||||
{year: 2013, count: 25},
|
||||
{year: 2014, count: 22},
|
||||
{year: 2015, count: 30},
|
||||
{year: 2016, count: 28},
|
||||
];
|
||||
|
||||
// linechart.data.labels.push(label);
|
||||
// linechart.data.datasets.forEach((dataset) => {
|
||||
// dataset.data.push(newData);
|
||||
// });
|
||||
// linechart.update();
|
||||
|
||||
window.linechart = new Chart(
|
||||
document.getElementById('acquisitions'),
|
||||
{
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: data.map(row => row.year),
|
||||
datasets: [
|
||||
{
|
||||
label: 'Acquisitions by year',
|
||||
data: data.map(row => row.count)
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
);
|
||||
6
src/darkmodetoggle.js
Executable file
6
src/darkmodetoggle.js
Executable file
@@ -0,0 +1,6 @@
|
||||
export default () => ({
|
||||
mode(modeName) {
|
||||
localStorage.theme = modeName;
|
||||
document.documentElement.classList.toggle("dark", modeName==='dark');
|
||||
}
|
||||
});
|
||||
844
src/generated.css
Normal file → Executable file
844
src/generated.css
Normal file → Executable file
File diff suppressed because one or more lines are too long
200
src/main.js
Normal file → Executable file
200
src/main.js
Normal file → Executable file
@@ -2,12 +2,17 @@ import './generated.css'
|
||||
|
||||
import Alpine from 'alpinejs'
|
||||
import persist from '@alpinejs/persist'
|
||||
import Chart from 'chart.js/auto'
|
||||
import darkmodetoggle from './darkmodetoggle';
|
||||
|
||||
window.Alpine = Alpine;
|
||||
Alpine.plugin(persist)
|
||||
|
||||
Alpine.data('darkmodetoggle', darkmodetoggle);
|
||||
Alpine.data('bookmeterList', function () {
|
||||
return {
|
||||
chart: null,
|
||||
chartPoints: null,
|
||||
chartPublisher: null,
|
||||
items: {},
|
||||
filteredItems: [],
|
||||
username: '',
|
||||
@@ -17,15 +22,19 @@ Alpine.data('bookmeterList', function () {
|
||||
nameFilter: '',
|
||||
years: [2023, 2024, 2025],
|
||||
year: this.$persist(['2025']).as('year-filter'),
|
||||
orderColumn: ['order', 1],
|
||||
orderColumn: ['order', 0],
|
||||
advancedSearch: this.$persist(0).as('advanced-search'),
|
||||
async init() {
|
||||
this.$watch('nameFilter', () => this.updateFilter());
|
||||
this.$watch('username', () => this.updateFilter());
|
||||
this.$watch('title', () => this.updateFilter());
|
||||
this.$watch('author', () => this.updateFilter());
|
||||
this.$watch('publisher', () => this.updateFilter());
|
||||
this.$watch('year', () => this.updateFilter());
|
||||
|
||||
this.$watch(() => JSON.stringify([
|
||||
this.nameFilter,
|
||||
this.username,
|
||||
this.title,
|
||||
this.author,
|
||||
this.publisher,
|
||||
this.year,
|
||||
this.advancedSearch
|
||||
]), () => this.updateFilter());
|
||||
|
||||
let qp = new URLSearchParams(window.location.search);
|
||||
if(qp.get('filter')) this.nameFilter = qp.get('filter');
|
||||
@@ -37,10 +46,129 @@ Alpine.data('bookmeterList', function () {
|
||||
])
|
||||
|
||||
this.items = [...data1, ...data2, ...data3]
|
||||
.filter(i => i.book.title.length > 0)
|
||||
.map(i => ({
|
||||
...i,
|
||||
_year: i.created_at.substring(0, 4),
|
||||
_username: i.username.toLowerCase(),
|
||||
_title: i.book.title.toLowerCase(),
|
||||
_author: i.book.author.toLowerCase(),
|
||||
_publisher: i.book.publisher.toLowerCase(),
|
||||
_search: (
|
||||
i.username +
|
||||
' ' + i.book.title +
|
||||
' ' + i.book.author +
|
||||
' ' + i.book.publisher
|
||||
).toLowerCase()
|
||||
}));
|
||||
this.updateFilter();
|
||||
// this.initChart();
|
||||
},
|
||||
updatechartnew(){
|
||||
this.chart.data.datasets[0].data[0] = Math.random()*50;
|
||||
Alpine.raw(this.chart).update('none');
|
||||
this.updateChart()
|
||||
},
|
||||
// initChart() {
|
||||
// const ctx = document.getElementById('statsChart').getContext('2d')
|
||||
//
|
||||
// this.chart = Alpine.raw(new Chart(ctx, {
|
||||
// type: 'bar',
|
||||
// data: {
|
||||
// labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
|
||||
// datasets: [{
|
||||
// label: '# of Votes',
|
||||
// data: [12, 19, 3, 5, 2, 3],
|
||||
// borderWidth: 1
|
||||
// }]
|
||||
// }
|
||||
// }));
|
||||
//
|
||||
// const chartPoints = document.getElementById('chartPoints').getContext('2d')
|
||||
//
|
||||
// const bubbleData = this.filteredItems.map(item => ({
|
||||
// x: item.book.pages,
|
||||
// y: item.book.rating,
|
||||
// r: Math.sqrt(item.likes || 1) * 2, // scale likes
|
||||
// format: item.book.format
|
||||
// }));
|
||||
//
|
||||
//
|
||||
// this.chartPoints = Alpine.raw(new Chart(chartPoints, {
|
||||
// type: 'bubble',
|
||||
// data: {
|
||||
// labels: [],
|
||||
// datasets: [{
|
||||
// label: 'points',
|
||||
// // data: this.pointsChartData,
|
||||
// data: bubbleData,
|
||||
// // backgroundColor: 'rgb(255, 99, 132)'
|
||||
// backgroundColor: bubbleData.map(d =>
|
||||
// d.format === 'audiobook' ? 'rgba(255,99,132,0.5)' :
|
||||
// d.format === 'ebook' ? 'rgba(54,162,235,0.5)' :
|
||||
// 'rgba(75,192,192,0.5)'
|
||||
// )
|
||||
// }]
|
||||
// },
|
||||
// options: {
|
||||
// scales: {
|
||||
// x: { title: { display: true, text: 'Pages' } },
|
||||
// y: { title: { display: true, text: 'Rating' }, min: 0, max: 10 }
|
||||
// }
|
||||
// }
|
||||
// }));
|
||||
//
|
||||
// },
|
||||
get pointsChartData(){
|
||||
let data = [];
|
||||
for(let i in this.filteredItems){
|
||||
// console.info(Alpine.raw(this.filteredItems[i]));
|
||||
if(!(this.filteredItems[i]['book']['rating'] in data)){
|
||||
data[this.filteredItems[i]['book']['rating']] = []
|
||||
}
|
||||
if(!(this.filteredItems[i]['likes'] in data[this.filteredItems[i]['book']['rating']])){
|
||||
data[this.filteredItems[i]['book']['rating']][this.filteredItems[i]['likes']] = 0;
|
||||
}
|
||||
|
||||
data[this.filteredItems[i]['book']['rating']][this.filteredItems[i]['likes']]++;
|
||||
}
|
||||
|
||||
let data2 = [];
|
||||
for(let i in data){
|
||||
for(let j in data[i]){
|
||||
data2.push({x: j, y: i, r: data[i][j], label: 'label'});
|
||||
}
|
||||
}
|
||||
return data2;
|
||||
},
|
||||
updateChart() {
|
||||
if (!this.chart) {
|
||||
return;
|
||||
}
|
||||
|
||||
// console.info( Alpine.raw(this.filteredItems));
|
||||
const topItems = this.filteredItems.slice(0, 20).map(item => ({
|
||||
title: item.book?.title || 'Unknown',
|
||||
likes: item.likes || 0,
|
||||
// date: (new Date(item.created_at)).getFullYear()+'-'+((new Date(item.created_at)).getMonth()+1)
|
||||
}));
|
||||
|
||||
let counts = {};
|
||||
|
||||
// topItems.forEach(item => counts[item.date] = 1 + ())
|
||||
|
||||
|
||||
console.info(topItems);
|
||||
this.chart.data.labels = topItems.map(i => i.title)
|
||||
this.chart.data.datasets[0].data = topItems.map(i => i.likes)
|
||||
|
||||
Alpine.raw(this.chart).update('none');
|
||||
},
|
||||
orderBy(property) {
|
||||
let direction = (this.orderColumn[0] === property) ? this.orderColumn[1] * -1 : 1;
|
||||
let direction = (this.orderColumn[0] === property) ? ((this.orderColumn[1] === -1) ? 0 : this.orderColumn[1] * -1) : 1;
|
||||
if (!direction) {
|
||||
property = 'order';
|
||||
}
|
||||
this.orderColumn = [property, direction];
|
||||
this.sort();
|
||||
},
|
||||
@@ -59,6 +187,10 @@ Alpine.data('bookmeterList', function () {
|
||||
};
|
||||
},
|
||||
sort() {
|
||||
if (this.orderColumn[1] === 0) {
|
||||
this.initFilteredData();
|
||||
return;
|
||||
}
|
||||
let sortFunction = ({
|
||||
username: (a,b) => ((a.username < b.username) ? -1 : (a.username > b.username) ? 1 : 0) * this.orderColumn[1],
|
||||
title: (a,b) => ((a.book.title < b.book.title) ? -1 : (a.book.title > b.book.title) ? 1 : 0) * this.orderColumn[1],
|
||||
@@ -70,29 +202,41 @@ Alpine.data('bookmeterList', function () {
|
||||
likes: (a,b) => ((a.likes < b.likes) ? -1 : (a.likes > b.likes) ? 1 : 0) * this.orderColumn[1],
|
||||
})[this.orderColumn[0]];
|
||||
this.filteredItems = this.filteredItems.sort(sortFunction);
|
||||
if (this.orderColumn[0] === 'order') {
|
||||
this.initFilteredData();
|
||||
}
|
||||
},
|
||||
updateFilter(){
|
||||
initFilteredData(){
|
||||
let filter = this.nameFilter.toLowerCase();
|
||||
this.filteredItems = this.items.filter(i => this.year.includes(i.created_at.substring(0,4)))
|
||||
.filter(i => {
|
||||
if(this.advancedSearch) {
|
||||
return (
|
||||
(!this.username || i.username.toLowerCase().includes(this.username.toLowerCase())) &&
|
||||
(!this.title || i.book.title.toLowerCase().includes(this.title.toLowerCase())) &&
|
||||
(!this.author || i.book.author.toLowerCase().includes(this.author.toLowerCase())) &&
|
||||
(!this.publisher || i.book.publisher.toLowerCase().includes(this.publisher.toLowerCase()))
|
||||
);
|
||||
} else {
|
||||
if(filter.startsWith('@') && i.username.toLowerCase().includes(filter.substring(1))) return true;
|
||||
if (i.username.toLowerCase().includes(filter) ||
|
||||
i.book.title.toLowerCase().includes(filter) || i.book.author.toLowerCase().includes(filter) || i.book.publisher.toLowerCase().includes(filter)) return true;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
this.sort();
|
||||
|
||||
this.updateURL();
|
||||
let username = this.username.toLowerCase().trim();
|
||||
let author = this.author.toLowerCase().trim();
|
||||
let publisher = this.publisher.toLowerCase().trim();
|
||||
let title = this.title.toLowerCase().trim();
|
||||
|
||||
this.filteredItems = this.items
|
||||
.filter(i => this.year.includes(i._year))
|
||||
.filter(i => {
|
||||
if (this.advancedSearch) {
|
||||
return (
|
||||
(!this.username || i._username.includes(this.username)) &&
|
||||
(!this.title || i._title.includes(title)) &&
|
||||
(!this.author || i._author.includes(author)) &&
|
||||
(!this.publisher || i._publisher.includes(publisher))
|
||||
);
|
||||
} else {
|
||||
if (filter.startsWith('@')) {
|
||||
return i.username.toLowerCase().includes(filter.slice(1));
|
||||
}
|
||||
return i._search.includes(filter);
|
||||
}
|
||||
});
|
||||
},
|
||||
updateFilter: Alpine.debounce(function () {
|
||||
this.initFilteredData();
|
||||
this.sort();
|
||||
this.updateURL();
|
||||
}, 250),
|
||||
updateURL() {
|
||||
let qp = new URLSearchParams();
|
||||
if(this.nameFilter !== '') qp.set('filter', this.nameFilter);
|
||||
|
||||
1
src/style.css
Normal file → Executable file
1
src/style.css
Normal file → Executable file
@@ -1,6 +1,7 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
@custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *));
|
||||
|
||||
[x-cloak] {
|
||||
display: none;
|
||||
|
||||
Reference in New Issue
Block a user