Add settings modal
This commit is contained in:
parent
18831d2e3f
commit
37a64cd488
|
@ -40,6 +40,7 @@
|
||||||
.app-modal {
|
.app-modal {
|
||||||
article {
|
article {
|
||||||
max-width: var(--max-width, none);
|
max-width: var(--max-width, none);
|
||||||
|
flex: 1 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
header {
|
header {
|
||||||
|
|
|
@ -43,7 +43,7 @@
|
||||||
<NoteTags {noteTags} bind:this={tagsComponent} />
|
<NoteTags {noteTags} bind:this={tagsComponent} />
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<div class="note-card__body">
|
<div class="note-card__body" class:note-card__body--editing={$editing}>
|
||||||
<NoteContent {note} {noteEditorStore} />
|
<NoteContent {note} {noteEditorStore} />
|
||||||
</div>
|
</div>
|
||||||
<footer class="note-card__footer">
|
<footer class="note-card__footer">
|
||||||
|
@ -94,6 +94,10 @@
|
||||||
&__body {
|
&__body {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
|
||||||
|
&--editing {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__footer {
|
&__footer {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import NoteCard from '$lib/components/notes/note-card.svelte';
|
import NoteCard from '$lib/components/notes/note-card.svelte';
|
||||||
import { notes } from '$lib/stores';
|
import { notes, settings } from '$lib/stores';
|
||||||
import { onDestroy, onMount } from 'svelte';
|
import { onDestroy, onMount } from 'svelte';
|
||||||
import { get, type Unsubscriber } from 'svelte/store';
|
import { get, type Unsubscriber } from 'svelte/store';
|
||||||
|
|
||||||
|
@ -15,6 +15,8 @@
|
||||||
let notesLimit = 100;
|
let notesLimit = 100;
|
||||||
let prevNotesCount = 0;
|
let prevNotesCount = 0;
|
||||||
|
|
||||||
|
const { noteScale } = settings;
|
||||||
|
let scaleUnsubscribe: Unsubscriber;
|
||||||
let countUnsubscribe: Unsubscriber;
|
let countUnsubscribe: Unsubscriber;
|
||||||
const resizeObserver = new ResizeObserver(updateVirtualScroll);
|
const resizeObserver = new ResizeObserver(updateVirtualScroll);
|
||||||
|
|
||||||
|
@ -24,7 +26,7 @@
|
||||||
.getComputedStyle(noteListElement)
|
.getComputedStyle(noteListElement)
|
||||||
.getPropertyValue('grid-template-columns')
|
.getPropertyValue('grid-template-columns')
|
||||||
.split(' ').length;
|
.split(' ').length;
|
||||||
const rowHeight = noteHeight + noteGap;
|
const rowHeight = Math.floor(noteHeight * get(noteScale)) + noteGap;
|
||||||
const fullHeight = Math.ceil(notesCount / columnCount) * rowHeight;
|
const fullHeight = Math.ceil(notesCount / columnCount) * rowHeight;
|
||||||
const windowHeight = window.innerHeight;
|
const windowHeight = window.innerHeight;
|
||||||
const scrollTop = window.scrollY + noteListElement.offsetTop;
|
const scrollTop = window.scrollY + noteListElement.offsetTop;
|
||||||
|
@ -46,12 +48,14 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
|
scaleUnsubscribe = settings.noteScale.subscribe(updateVirtualScroll);
|
||||||
countUnsubscribe = notes.count.subscribe(updateVirtualScroll);
|
countUnsubscribe = notes.count.subscribe(updateVirtualScroll);
|
||||||
resizeObserver.observe(noteListElement);
|
resizeObserver.observe(noteListElement);
|
||||||
window.addEventListener('scroll', updateVirtualScroll);
|
window.addEventListener('scroll', updateVirtualScroll);
|
||||||
});
|
});
|
||||||
|
|
||||||
onDestroy(() => {
|
onDestroy(() => {
|
||||||
|
scaleUnsubscribe();
|
||||||
countUnsubscribe();
|
countUnsubscribe();
|
||||||
resizeObserver.disconnect();
|
resizeObserver.disconnect();
|
||||||
window.removeEventListener('scroll', updateVirtualScroll);
|
window.removeEventListener('scroll', updateVirtualScroll);
|
||||||
|
@ -61,7 +65,7 @@
|
||||||
<ul
|
<ul
|
||||||
bind:this={noteListElement}
|
bind:this={noteListElement}
|
||||||
class="note-list"
|
class="note-list"
|
||||||
style:--note-height={`${noteHeight}px`}
|
style:--note-height={`${Math.floor(noteHeight * $noteScale)}px`}
|
||||||
style:--note-gap={`${noteGap}px`}
|
style:--note-gap={`${noteGap}px`}
|
||||||
style:--scroll-height={`${scrollHeight}px`}
|
style:--scroll-height={`${scrollHeight}px`}
|
||||||
style:--scroll-offset={`translateY(${scrollOffset}px)`}
|
style:--scroll-offset={`translateY(${scrollOffset}px)`}
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import Button from '../button.svelte';
|
||||||
|
import FaGear from '$icon/fa-gear.svelte';
|
||||||
|
import { settings } from '$lib/stores';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Button plain icon size="lg" label="Settings" on:click={() => settings.open.set(true)}>
|
||||||
|
<FaGear />
|
||||||
|
</Button>
|
|
@ -0,0 +1,21 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import Modal from '../modal.svelte';
|
||||||
|
import { settings } from '$lib/stores';
|
||||||
|
|
||||||
|
const { open, noteScale } = settings;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Modal title="Settings" size="sm" bind:open={$open}>
|
||||||
|
<label>
|
||||||
|
Note Size
|
||||||
|
<input
|
||||||
|
type="range"
|
||||||
|
name="noteScale"
|
||||||
|
min="0.80"
|
||||||
|
max="1.50"
|
||||||
|
step="0.05"
|
||||||
|
bind:value={$noteScale}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<!-- TODO: setting for default note color -->
|
||||||
|
</Modal>
|
|
@ -2,9 +2,12 @@ import { derived } from 'svelte/store';
|
||||||
import { NotesStore } from './notes';
|
import { NotesStore } from './notes';
|
||||||
import { ThemeStore, type Theme } from './theme';
|
import { ThemeStore, type Theme } from './theme';
|
||||||
import { TagsStore } from './tags';
|
import { TagsStore } from './tags';
|
||||||
|
import { SettingsStore } from './settings';
|
||||||
|
|
||||||
export const tags = new TagsStore();
|
export const tags = new TagsStore();
|
||||||
export const notes = new NotesStore(tags);
|
export const notes = new NotesStore(tags);
|
||||||
|
|
||||||
|
export const settings = new SettingsStore();
|
||||||
|
|
||||||
export const theme = new ThemeStore();
|
export const theme = new ThemeStore();
|
||||||
export const themeUpperCase = derived(theme, ($theme) => $theme.toUpperCase() as Uppercase<Theme>);
|
export const themeUpperCase = derived(theme, ($theme) => $theme.toUpperCase() as Uppercase<Theme>);
|
||||||
|
|
|
@ -3,6 +3,7 @@ import type { Schema } from '$lib/db/schema';
|
||||||
import type { Insertable, Selectable, Updateable } from 'kysely';
|
import type { Insertable, Selectable, Updateable } from 'kysely';
|
||||||
import { writable, type Readable, get, readonly } from 'svelte/store';
|
import { writable, type Readable, get, readonly } from 'svelte/store';
|
||||||
import type { TagsStore } from './tags';
|
import type { TagsStore } from './tags';
|
||||||
|
import { settings } from '.';
|
||||||
|
|
||||||
export type Note = Selectable<Schema['note']>;
|
export type Note = Selectable<Schema['note']>;
|
||||||
export type NewNote = Insertable<Schema['note']>;
|
export type NewNote = Insertable<Schema['note']>;
|
||||||
|
@ -68,7 +69,7 @@ export class NotesStore implements Readable<Note[]> {
|
||||||
add = async (newNote?: NewNote, scrollToTop?: boolean) => {
|
add = async (newNote?: NewNote, scrollToTop?: boolean) => {
|
||||||
if (!newNote) {
|
if (!newNote) {
|
||||||
newNote = {
|
newNote = {
|
||||||
color: 'YELLOW', // TODO: add way to set the default color
|
color: get(settings.defaultNoteColor),
|
||||||
content: '',
|
content: '',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
import type { NoteColor } from '$lib/db/enums/note-color';
|
||||||
|
import { writable } from 'svelte/store';
|
||||||
|
|
||||||
|
export class SettingsStore {
|
||||||
|
open = writable<boolean>(false);
|
||||||
|
|
||||||
|
noteScale = writable<number>(localStorage.noteScale ? parseFloat(localStorage.noteScale) : 1);
|
||||||
|
defaultNoteColor = writable<keyof typeof NoteColor>(localStorage.defaultNoteColor ?? 'YELLOW');
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.noteScale.subscribe((value) => (localStorage.noteScale = value.toString()));
|
||||||
|
this.defaultNoteColor.subscribe((value) => (localStorage.defaultNoteColor = value));
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,8 @@
|
||||||
import NoteAddButton from '$lib/components/notes/note-add-button.svelte';
|
import NoteAddButton from '$lib/components/notes/note-add-button.svelte';
|
||||||
import NoteListPlaceholder from '$lib/components/notes/note-list-placeholder.svelte';
|
import NoteListPlaceholder from '$lib/components/notes/note-list-placeholder.svelte';
|
||||||
import NoteList from '$lib/components/notes/note-list.svelte';
|
import NoteList from '$lib/components/notes/note-list.svelte';
|
||||||
|
import SettingsButton from '$lib/components/settings/settings-button.svelte';
|
||||||
|
import SettingsModal from '$lib/components/settings/settings-modal.svelte';
|
||||||
import ThemeToggle from '$lib/components/theme-toggle.svelte';
|
import ThemeToggle from '$lib/components/theme-toggle.svelte';
|
||||||
import { notes, tags } from '$lib/stores';
|
import { notes, tags } from '$lib/stores';
|
||||||
|
|
||||||
|
@ -10,8 +12,9 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<header>
|
<header>
|
||||||
<h1>Notes</h1>
|
|
||||||
<ThemeToggle />
|
<ThemeToggle />
|
||||||
|
<h1>Notes</h1>
|
||||||
|
<SettingsButton />
|
||||||
</header>
|
</header>
|
||||||
<main>
|
<main>
|
||||||
<section hidden={$tags.length === 0}>
|
<section hidden={$tags.length === 0}>
|
||||||
|
@ -26,13 +29,14 @@
|
||||||
<NoteAddButton />
|
<NoteAddButton />
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
|
<SettingsModal />
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
header {
|
header {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
gap: 1.2rem;
|
gap: 2rem;
|
||||||
margin: 1.5rem;
|
margin: 1.5rem;
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
|
|
Loading…
Reference in New Issue