Change settings button position

This commit is contained in:
franzz committed 2026-09-20 00:21:30 +02:00
1 parent 78c1d8e844
commit 8db3b3d363
12 files changed
+240 -48

No files matched your search

+1 -1
View File
@@ -47,7 +47,7 @@
"loading": "Turning pages…", "loading": "Turning pages…",
"today": "Today", "today": "Today",
"go_to_writing": "Back to today's page", "go_to_writing": "Back to today's page",
"empty_entry": "Blank page", "new_entry": "Start a new page",
"earlier": "Earlier entries", "earlier": "Earlier entries",
"later": "Later entries" "later": "Later entries"
}, },
+1 -1
View File
@@ -47,7 +47,7 @@
"loading": "On tourne les pages…", "loading": "On tourne les pages…",
"today": "Aujourd'hui", "today": "Aujourd'hui",
"go_to_writing": "Revenir à la page du jour", "go_to_writing": "Revenir à la page du jour",
"empty_entry": "Page blanche", "new_entry": "Commencer une nouvelle page",
"earlier": "Entrées précédentes", "earlier": "Entrées précédentes",
"later": "Entrées suivantes" "later": "Entrées suivantes"
}, },
+51 -2
View File
@@ -35,6 +35,7 @@ export default {
bCalendarOpen: false, bCalendarOpen: false,
bSettingsOpen: false, bSettingsOpen: false,
bMenuOpen: false, bMenuOpen: false,
bSigningOut: false,
bRailOpen: false, bRailOpen: false,
iCurrentId: 0, iCurrentId: 0,
sCurrentDay: '', sCurrentDay: '',
@@ -127,13 +128,51 @@ export default {
this.$refs.book?.goToWriting(); this.$refs.book?.goToWriting();
}, },
/**
* The + at the end of the rail: seal the page being written and turn to
* a fresh one. Landing there should mean you can simply write, the same
* way opening the book does.
*/
async onNewEntry() {
this.bRailOpen = false;
try {
await this.journal.startNewEntry();
this.$nextTick(() => this.$refs.book?.goToWriting());
}
catch(oError) {
this.notify(oError.desc_lang_text || oError.message, true);
}
},
/* Account */ /* Account */
onSignedIn(asUser) { onSignedIn(asUser) {
Object.assign(this.user, asUser); Object.assign(this.user, asUser);
}, },
async onSignedOut() { /**
* The logout call is what ends the session; the local reset only
* follows it, so a failed call leaves the reader where they were.
*/
async signOut() {
if(this.bSigningOut) return;
this.bSigningOut = true;
try {
await this.api.post('logout');
this.clearSession();
}
catch(oError) {
this.notify(oError.desc_lang_text || oError.message || this.lang.get('error.unexpected'), true);
}
finally {
this.bSigningOut = false;
}
},
clearSession() {
this.bSettingsOpen = false; this.bSettingsOpen = false;
this.bMenuOpen = false; this.bMenuOpen = false;
@@ -259,6 +298,16 @@ export default {
<appIcon icon="settings" /> <appIcon icon="settings" />
<span>{{ lang.get('account.settings') }}</span> <span>{{ lang.get('account.settings') }}</span>
</button> </button>
<button
type="button"
class="account-menu__item account-menu__item--bad"
:disabled="bSigningOut"
@click="signOut"
>
<appIcon icon="signOut" />
<span>{{ lang.get('account.sign_out') }}</span>
</button>
</div> </div>
</div> </div>
</template> </template>
@@ -287,6 +336,7 @@ export default {
:loading="journal.loading" :loading="journal.loading"
@pick="onPickBookmark" @pick="onPickBookmark"
@load-older="onLoadOlder" @load-older="onLoadOlder"
@new="onNewEntry"
/> />
</div> </div>
</main> </main>
@@ -304,7 +354,6 @@ export default {
<settingsPanel <settingsPanel
v-if="bSettingsOpen" v-if="bSettingsOpen"
@close="bSettingsOpen = false" @close="bSettingsOpen = false"
@signed-out="onSignedOut"
/> />
</div> </div>
</template> </template>
+33 -3
View File
@@ -40,6 +40,10 @@ const RAIL_REACH = 3;
* Each is stamped with the date and time the entry was started, in the timezone * Each is stamped with the date and time the entry was started, in the timezone
* it was written in - so a book kept across a move still reads as the local * it was written in - so a book kept across a move still reads as the local
* time of each moment. Clicking one turns the book to that page. * time of each moment. Clicking one turns the book to that page.
*
* A page nothing has been written on is not one of them: an empty tab marks
* nothing, and a book of them would be a rail of blanks. The + at the end of
* the rail stands there instead, and starts the page it would have marked.
*/ */
export default { export default {
components: { components: {
@@ -54,15 +58,18 @@ export default {
hasOlder: {type: Boolean, default: false}, hasOlder: {type: Boolean, default: false},
loading: {type: Boolean, default: false} loading: {type: Boolean, default: false}
}, },
emits: ['pick', 'load-older'], emits: ['pick', 'load-older', 'new'],
inject: ['lang'], inject: ['lang'],
computed: { computed: {
tabs() { tabs() {
return this.bookmarks.map((oBookmark) => ({ //A bookmark is a page with something on it. The one page that can be
//empty is the one being written, and it gets its tab with its first
//words - closed entries are never blank, the book discards those.
return this.bookmarks.filter((oBookmark) => (oBookmark.preview || '') !== '').map((oBookmark) => ({
id: oBookmark.id, id: oBookmark.id,
date: formatShortDate(oBookmark.time, oBookmark.timezone, this.lang.locale), date: formatShortDate(oBookmark.time, oBookmark.timezone, this.lang.locale),
time: formatTime(oBookmark.time, oBookmark.timezone, this.lang.locale), time: formatTime(oBookmark.time, oBookmark.timezone, this.lang.locale),
preview: oBookmark.preview || this.lang.get('book.empty_entry'), preview: oBookmark.preview,
open: (oBookmark.status === 'open'), open: (oBookmark.status === 'open'),
dye: dyeForDay(getDayKey(oBookmark.time, oBookmark.timezone)) dye: dyeForDay(getDayKey(oBookmark.time, oBookmark.timezone))
})); }));
@@ -89,6 +96,16 @@ export default {
}, },
hasAfter() { hasAfter() {
return (this.shown.length > 0) && (this.shown.at(-1).id !== this.tabs.at(-1)?.id); return (this.shown.length > 0) && (this.shown.at(-1).id !== this.tabs.at(-1)?.id);
},
/**
* Whether the book is open at a page still to be written on.
*
* That page has no tab, so the + is what marks it - and the rail would
* otherwise show nothing at all as the place you are reading.
*/
onBlankPage() {
const oOpen = this.bookmarks.find((oBookmark) => oBookmark.id === this.openId);
return (this.currentId === this.openId) && (oOpen !== undefined) && ((oOpen.preview || '') === '');
} }
}, },
watch: { watch: {
@@ -162,6 +179,19 @@ export default {
</span> </span>
<span class="rail__preview">{{ oTab.preview }}</span> <span class="rail__preview">{{ oTab.preview }}</span>
</button> </button>
<!-- Straight after the last tab: the page that has not been started
yet, where its own tab will be once there is something on it. -->
<button
type="button"
class="rail__new"
:class="onBlankPage ? 'rail__new--current' : null"
:title="lang.get('book.new_entry')"
:aria-label="lang.get('book.new_entry')"
@click="$emit('new')"
>
<appIcon icon="plus" size="1.1em" />
</button>
</div> </div>
<button <button
+1 -17
View File
@@ -13,7 +13,7 @@ export default {
components: { components: {
appIcon appIcon
}, },
emits: ['close', 'signed-out'], emits: ['close'],
inject: ['api', 'consts', 'lang', 'user'], inject: ['api', 'consts', 'lang', 'user'],
data() { data() {
return { return {
@@ -90,19 +90,6 @@ export default {
saveName() { saveName() {
const sName = this.sName.trim(); const sName = this.sName.trim();
if((sName !== '') && (sName !== this.user.name)) this.set('name', sName); if((sName !== '') && (sName !== this.user.name)) this.set('name', sName);
},
async signOut() {
this.bBusy = true;
try {
await this.api.post('logout');
this.$emit('signed-out');
}
catch(oError) {
this.sError = oError.desc_lang_text || oError.message;
}
finally {
this.bBusy = false;
}
} }
} }
}; };
@@ -188,9 +175,6 @@ export default {
<appIcon icon="check" /> <appIcon icon="check" />
<span>{{ lang.get('action.close') }}</span> <span>{{ lang.get('action.close') }}</span>
</button> </button>
<button type="button" class="text-button text-button--bad" :disabled="bBusy" @click="signOut">
<span>{{ lang.get('account.sign_out') }}</span>
</button>
</div> </div>
</div> </div>
</div> </div>
+5 -9
View File
@@ -38,16 +38,10 @@ const asIcons = {
'M12 11.6a3.6 3.6 0 1 0 0-7.2 3.6 3.6 0 0 0 0 7.2z', 'M12 11.6a3.6 3.6 0 1 0 0-7.2 3.6 3.6 0 0 0 0 7.2z',
'M4.8 20c.6-3.7 3.5-5.8 7.2-5.8s6.6 2.1 7.2 5.8' 'M4.8 20c.6-3.7 3.5-5.8 7.2-5.8s6.6 2.1 7.2 5.8'
], ],
//A cog: six teeth around a hub
settings: [ settings: [
'M12 15.2a3.2 3.2 0 1 0 0-6.4 3.2 3.2 0 0 0 0 6.4z', 'M10.3 6L10.4 3.1h3.2L13.7 6a6.2 6.2 0 0 1 2.6 1.5l2.6-1.3 1.6 2.7-2.5 1.6a6.2 6.2 0 0 1 0 3l2.5 1.6-1.6 2.7-2.6-1.3a6.2 6.2 0 0 1-2.6 1.5l-.1 2.9h-3.2L10.3 18a6.2 6.2 0 0 1-2.6-1.5l-2.6 1.3-1.6-2.7 2.5-1.6a6.2 6.2 0 0 1 0-3L3.5 8.9l1.6-2.7 2.6 1.3A6.2 6.2 0 0 1 10.3 6z',
'M12 3v2.3', 'M12 14.8a2.8 2.8 0 1 0 0-5.6 2.8 2.8 0 0 0 0 5.6z'
'M12 18.7V21',
'M3 12h2.3',
'M18.7 12H21',
'M5.6 5.6l1.7 1.7',
'M16.7 16.7l1.7 1.7',
'M18.4 5.6l-1.7 1.7',
'M7.3 16.7l-1.7 1.7'
], ],
signOut: [ signOut: [
'M14.4 8V5.2H5.6v13.6h8.8V16', 'M14.4 8V5.2H5.6v13.6h8.8V16',
@@ -61,6 +55,8 @@ const asIcons = {
'M10.5 10.9v5.7', 'M10.5 10.9v5.7',
'M13.5 10.9v5.7' 'M13.5 10.9v5.7'
], ],
//A blank page waiting to be started
plus: ['M12 5.6v12.8', 'M5.6 12h12.8'],
close: ['M6.6 6.6l10.8 10.8', 'M17.4 6.6 6.6 17.4'], close: ['M6.6 6.6l10.8 10.8', 'M17.4 6.6 6.6 17.4'],
check: ['M5.6 12.7l4.2 4.2 8.6-9.6'], check: ['M5.6 12.7l4.2 4.2 8.6-9.6'],
alert: [ alert: [
+27
View File
@@ -172,6 +172,33 @@ export default class Journal {
return oEntry; return oEntry;
} }
/**
* Start a page of one's own, rather than writing on in the last one.
*
* A page nothing has been written on is already that page, so it is handed
* straight back - it is only a page with writing on it that has to be sealed
* first, which is what makes the next one a new entry and not the same
* thought carried on.
*/
async startNewEntry() {
const oEntry = this.openEntry;
if(oEntry && (oEntry.content.trim() === '')) return oEntry;
//The close carries the content itself, but the save state goes with it -
//flushing here is what keeps the indicator from sitting on "unsaved"
//about a page that has already been sealed.
if(this.isDirty) await this.save();
await this.closeEntry();
//A new page goes at the end of the book, so the loaded window has to be
//the end of the book before one can be appended to it - reading back in
//the journal would otherwise slot it into the middle of the flow.
if(this.hasNewer) await this.load();
return this.startWriting();
}
write(sContent) { write(sContent) {
const oEntry = this.openEntry; const oEntry = this.openEntry;
if(!oEntry) return; if(!oEntry) return;
+10
View File
@@ -206,3 +206,13 @@
background-color: color.$paper-shade; background-color: color.$paper-shade;
} }
} }
.account-menu__item--bad {
color: color.$bad;
&:hover,
&:focus-visible {
color: color.$bad;
background-color: rgba(155, 70, 49, 0.09);
}
}
+17 -5
View File
@@ -170,28 +170,40 @@
border-right: 1px solid color.$rule-margin; border-right: 1px solid color.$rule-margin;
} }
/* Where an entry begins, the margin says when it was written */ /* Where an entry begins, the margin says when it was written.
*
* Sized off the ruled line like the writing it annotates, so it follows the
* hand in use instead of holding one fixed size across six of them - a stamp
* in a wide hand at a fixed size is wider than the column it sits in. Both
* spans are led at half a rule, so the usual date-over-time comes to exactly
* one ruled line and a date long enough to wrap still lands on the book's
* rhythm rather than between it. */
.page__stamp { .page__stamp {
--stamp-size: calc(var(--book-line) * #{var.$hand-scale} * 0.5);
position: absolute; position: absolute;
right: 0.7rem; right: 0.7rem;
width: calc(100% - 0.7rem); width: calc(100% - 0.7rem);
text-align: right; text-align: right;
font-family: var.$font-hand; font-family: var.$font-hand;
line-height: 1.05; line-height: calc(var(--book-line) / 2);
color: color.$ink-soft; color: color.$ink-soft;
pointer-events: none; pointer-events: none;
} }
.page__stamp-date { .page__stamp-date {
display: block; display: block;
font-size: 1.05rem; font-size: var(--stamp-size);
font-weight: 600; font-weight: 600;
white-space: nowrap;
//A written-out month in a wide hand, or a locale that spells the date out
//at length, wraps inside the margin instead of spilling over the page.
overflow-wrap: break-word;
} }
.page__stamp-time { .page__stamp-time {
display: block; display: block;
font-size: 0.95rem; font-size: calc(var(--stamp-size) * 0.9);
color: color.$ink-faint; color: color.$ink-faint;
} }
+40 -9
View File
@@ -77,23 +77,19 @@
} }
.page__margin { .page__margin {
flex-basis: 3.1rem; flex-basis: 4.1rem;
padding-right: 0.4rem; padding-right: 0.4rem;
} }
//A phone gives the margin less room, so the stamp is written smaller in it
//rather than wider than it.
.page__stamp { .page__stamp {
--stamp-size: calc(var(--book-line) * #{var.$hand-scale} * 0.42);
right: 0.4rem; right: 0.4rem;
width: calc(100% - 0.4rem); width: calc(100% - 0.4rem);
} }
.page__stamp-date {
font-size: 0.9rem;
}
.page__stamp-time {
font-size: 0.82rem;
}
.page__column, .page__column,
.page__measure, .page__measure,
.page__input, .page__input,
@@ -175,6 +171,41 @@
} }
} }
/* In the drawer there is no page block for a tab to come out from under, so
* the + is a card like the tabs are - drawn in the light ink the cover asks
* for rather than in paper tones, and it stays put instead of sliding out. */
.rail__new {
width: 100%;
min-height: 2.4rem;
margin-left: 0;
padding-left: 0;
color: rgba(254, 244, 225, 0.6);
background-color: rgba(254, 244, 225, 0.06);
border: 1px dashed rgba(254, 244, 225, 0.3);
border-radius: var.$block-radius;
&:hover,
&:focus-visible {
transform: none;
color: color.$paper;
background-color: rgba(254, 244, 225, 0.12);
}
}
.rail__new--current {
transform: none;
color: color.$paper;
background-color: rgba(254, 244, 225, 0.14);
border-style: solid;
border-color: rgba(254, 244, 225, 0.35);
box-shadow: inset -0.2rem 0 0 color.$ribbon;
&:hover,
&:focus-visible {
transform: none;
}
}
.calendar { .calendar {
width: min(19rem, calc(100vw - 1.5rem)); width: min(19rem, calc(100vw - 1.5rem));
} }
+52
View File
@@ -159,6 +159,58 @@
white-space: nowrap; white-space: nowrap;
} }
/* The page nobody has written on yet.
*
* Shaped like a tab, because that is what it becomes the moment something is
* written on it - but a short one, drawn in outline: there is no date to stamp
* on it and no day for it to take a dye from. It stands in the stack directly
* after the last tab, which is where its own tab will be.
*/
.rail__new {
flex: 0 0 auto;
align-self: flex-start;
display: flex;
align-items: center;
justify-content: center;
width: 4.75rem;
min-height: 1.9rem;
//The list's own gap sets it off from the last tab
margin-left: calc(-1 * var.$tab-tuck);
padding-left: var.$tab-tuck;
color: color.$ink-faint;
background-color: rgba(251, 241, 220, 0.5);
//No left edge: like a tab, it comes out from under the page block
border: 1px dashed color.$paper-deep;
border-left: 0;
border-radius: 0 var.$block-radius var.$block-radius 0;
transition: transform var.$trans-mid ease-out, background-color var.$trans-quick, color var.$trans-quick;
&:hover,
&:focus-visible {
transform: translateX(0.45rem);
background-color: color.$paper;
color: color.$ink-soft;
}
}
/* The book is open at that page, so the + is doing a bookmark's job - and wears
* the same ribbon the tab of the page being written on would. */
.rail__new--current {
transform: translateX(0.3rem);
color: color.$ink-soft;
background-color: color.$paper;
border-style: solid;
border-color: color.$paper-edge;
box-shadow: 0.12rem 0.15rem 0.5rem rgba(90, 54, 31, 0.22), inset -0.2rem 0 0 color.$ribbon;
&:hover,
&:focus-visible {
transform: translateX(0.65rem);
}
}
/* The rail shows a few tabs either side of where the reading is. These two /* The rail shows a few tabs either side of where the reading is. These two
* say the book carries on past them, and walk the window along. */ * say the book carries on past them, and walk the window along. */
.rail__more { .rail__more {
+2 -1
View File
@@ -14,7 +14,8 @@ $block-spacing: 1rem;
$block-radius: 3px; $block-radius: 3px;
//The book //The book
$page-margin-width: 4.25rem; //the left column that carries date stamps $page-margin-width: 5.25rem; //the left column that carries date stamps: wide
//enough for a written-out date in the widest hand
$page-padding: 2.25rem; $page-padding: 2.25rem;
$spine-width: 2.5rem; $spine-width: 2.5rem;
$book-max-width: 72rem; //leaves the rail room to show its dates $book-max-width: 72rem; //leaves the rail room to show its dates