Introduce our own Container, Row, and Col components (#1495)

* Refactor out Placeholder from CardInfo.svelte

* Add breakpoint parameter for Container

- Use `Container` component inside `TitledContainer`

* Build Item into Row

- Use Row in DeckOptionsPage instead of just Item

* Reengineer Container/Row/Col CSS

* Inline Badges next to Labels when Lable spans multiple rows

* Adjust margins for mobile

* Implement Col component breakpoints

* Move card-info to use new Container and Row components

* Join StickyHeader and StickyFooter to StickyContainer

* Remove default middle vertical-alignment for Badges again

* Satisfy tests

* Restore inline gutters in change-notetype Mapper

* Add some comment to Col and Container

* Fix breaking behavior in DeckOptionsPage when multi-column

* Add back toolbar left padding to counter-act buttongroup right margins

* Make Label in SwitchRow take more of available space
This commit is contained in:
Henrik Giesel 2021-11-17 04:49:52 +01:00 committed by GitHub
parent 98b527eb99
commit ab6a68ec49
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
48 changed files with 668 additions and 511 deletions

View file

@ -51,6 +51,14 @@ sass_library(
visibility = ["//visibility:public"], visibility = ["//visibility:public"],
) )
sass_library(
name = "breakpoints_lib",
srcs = [
"breakpoints.scss",
],
visibility = ["//visibility:public"],
)
sass_library( sass_library(
name = "button_mixins_lib", name = "button_mixins_lib",
srcs = [ srcs = [

View file

@ -1,3 +1,5 @@
/* Copyright: Ankitects Pty Ltd and contributors
* License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */
@use "fusion-vars"; @use "fusion-vars";
@import "bootstrap/scss/functions"; @import "bootstrap/scss/functions";

93
sass/breakpoints.scss Normal file
View file

@ -0,0 +1,93 @@
/* Copyright: Ankitects Pty Ltd and contributors
* License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */
@use "sass:list";
@use "sass:map";
$bps: (
"xs",
"sm",
"md",
"lg",
"xl",
"xxl",
);
$breakpoints: (
list.nth($bps, 2): 576px,
list.nth($bps, 3): 768px,
list.nth($bps, 4): 992px,
list.nth($bps, 5): 1200px,
list.nth($bps, 6): 1400px,
);
@mixin with-breakpoint($bp) {
@if map.get($breakpoints, $bp) {
@media (min-width: map.get($breakpoints, $bp)) {
@content;
}
} @else {
@content;
}
};
@mixin with-breakpoints($prefix, $dict) {
@each $property, $values in $dict {
@each $bp, $value in $values {
@if map.get($breakpoints, $bp) {
@media (min-width: map.get($breakpoints, $bp)) {
.#{$prefix}-#{$bp} {
#{$property}: $value;
}
}
} @else {
.#{$prefix}-#{$bp} {
#{$property}: $value;
}
}
}
}
};
@function breakpoints-upto($upto) {
$result: ();
@each $bp in $bps {
$result: list.append($result, $bp);
@if $bp == $upto {
@return $result;
}
}
@return $result;
}
@function breakpoint-selector-upto($prefix, $upto) {
$result: ();
@each $bp in breakpoints-upto($upto) {
$result: list.append($result, ".#{$prefix}-#{$bp}", $separator: comma)
}
@return $result;
}
@mixin with-breakpoints-upto($prefix, $dict) {
@each $property, $values in $dict {
@each $bp, $value in $values {
$selector: breakpoint-selector-upto($prefix, $bp);
@if map.get($breakpoints, $bp) {
@media (min-width: map.get($breakpoints, $bp)) {
#{$selector} {
#{$property}: $value;
}
}
} @else {
#{$selector} {
#{$property}: $value;
}
}
}
}
};

View file

@ -3,53 +3,54 @@ Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
--> -->
<script lang="ts"> <script lang="ts">
import * as tr from "../lib/ftl";
import type { Stats } from "../lib/proto"; import type { Stats } from "../lib/proto";
import { getCardStats } from "./lib"; import { getCardStats } from "./lib";
import Container from "../components/Container.svelte";
import Row from "../components/Row.svelte";
import CardStats from "./CardStats.svelte"; import CardStats from "./CardStats.svelte";
import CardInfoPlaceholder from "./CardInfoPlaceholder.svelte";
import Revlog from "./Revlog.svelte"; import Revlog from "./Revlog.svelte";
export let cardId: number | null = null; export let cardId: number | null = null;
export let includeRevlog: boolean = true; export let includeRevlog: boolean = true;
let stats: Stats.CardStatsResponse | null = null; let stats: Stats.CardStatsResponse | null = null;
let revlog: Stats.CardStatsResponse.StatsRevlogEntry[] | null = null;
$: if (cardId === null) { async function updateStats(cardId: number): Promise<void> {
stats = null;
} else {
const requestedCardId = cardId; const requestedCardId = cardId;
getCardStats(requestedCardId).then((s) => { const cardStats = await getCardStats(requestedCardId);
/* Skip if another update has been triggered in the meantime. */
if (requestedCardId === cardId) { /* Skip if another update has been triggered in the meantime. */
stats = s; if (requestedCardId === cardId) {
stats = cardStats;
if (includeRevlog) {
revlog = stats.revlog as Stats.CardStatsResponse.StatsRevlogEntry[];
} }
}); }
}
$: if (cardId) {
updateStats(cardId);
} else {
stats = null;
revlog = null;
} }
</script> </script>
{#if stats !== null} <Container breakpoint="md" --gutter-inline="0.25rem" --gutter-block="0.5rem">
<div class="container"> {#if stats}
<div> <Row>
<CardStats {stats} /> <CardStats {stats} />
{#if includeRevlog} </Row>
<Revlog {stats} />
{/if}
</div>
</div>
{:else}
<div class="placeholder">{tr.cardStatsNoCard()}</div>
{/if}
<style> {#if revlog}
.container { <Row>
max-width: 40em; <Revlog {revlog} />
} </Row>
{/if}
.placeholder { {:else}
margin: 0; <CardInfoPlaceholder />
position: absolute; {/if}
top: 50%; </Container>
left: 50%;
transform: translate(-50%, -50%);
}
</style>

View file

@ -0,0 +1,19 @@
<!--
Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
<script lang="ts">
import * as tr from "../lib/ftl";
</script>
<div class="card-info-placeholder">{tr.cardStatsNoCard()}</div>
<style lang="scss">
.card-info-placeholder {
margin: 0;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
</style>

View file

@ -94,10 +94,10 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
$: statsRows = rowsFromStats(stats); $: statsRows = rowsFromStats(stats);
</script> </script>
<table class="stats-table"> <table class="stats-table align-start">
{#each statsRows as row, _index} {#each statsRows as row}
<tr> <tr>
<th style="text-align:start">{row.label}</th> <th class="align-start">{row.label}</th>
<td>{row.value}</td> <td>{row.value}</td>
</tr> </tr>
{/each} {/each}
@ -108,6 +108,9 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
width: 100%; width: 100%;
border-spacing: 1em 0; border-spacing: 1em 0;
border-collapse: collapse; border-collapse: collapse;
}
.align-start {
text-align: start; text-align: start;
} }
</style> </style>

View file

@ -7,10 +7,10 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import { Stats } from "../lib/proto"; import { Stats } from "../lib/proto";
import { Timestamp, timeSpan } from "../lib/time"; import { Timestamp, timeSpan } from "../lib/time";
export let stats: Stats.CardStatsResponse;
type StatsRevlogEntry = Stats.CardStatsResponse.StatsRevlogEntry; type StatsRevlogEntry = Stats.CardStatsResponse.StatsRevlogEntry;
export let revlog: StatsRevlogEntry[];
function reviewKindClass(entry: StatsRevlogEntry): string { function reviewKindClass(entry: StatsRevlogEntry): string {
switch (entry.reviewKind) { switch (entry.reviewKind) {
case Stats.RevlogEntry.ReviewKind.LEARNING: case Stats.RevlogEntry.ReviewKind.LEARNING:
@ -59,6 +59,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
function revlogRowFromEntry(entry: StatsRevlogEntry): RevlogRow { function revlogRowFromEntry(entry: StatsRevlogEntry): RevlogRow {
const timestamp = new Timestamp(entry.time); const timestamp = new Timestamp(entry.time);
return { return {
date: timestamp.dateString(), date: timestamp.dateString(),
time: timestamp.timeString(), time: timestamp.timeString(),
@ -72,37 +73,32 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
}; };
} }
let revlogRows: RevlogRow[]; $: revlogRows = revlog.map(revlogRowFromEntry);
$: revlogRows = stats.revlog.map((entry) =>
revlogRowFromEntry(entry as StatsRevlogEntry),
);
</script> </script>
{#if stats.revlog.length} {#if revlog.length > 0}
<div class="revlog-container"> <table class="revlog-table">
<table class="revlog-table"> <tr>
<th class="left">{tr2.cardStatsReviewLogDate()}</th>
<th class="center hidden-xs">{tr2.cardStatsReviewLogType()}</th>
<th class="center">{tr2.cardStatsReviewLogRating()}</th>
<th class="right">{tr2.cardStatsInterval()}</th>
<th class="center hidden-xs">{tr2.cardStatsEase()}</th>
<th class="right">{tr2.cardStatsReviewLogTimeTaken()}</th>
</tr>
{#each revlogRows as row, _index}
<tr> <tr>
<th class="left">{tr2.cardStatsReviewLogDate()}</th> <td class="left"><b>{row.date}</b> @ {row.time}</td>
<th class="center hidden-xs">{tr2.cardStatsReviewLogType()}</th> <td class="center hidden-xs {row.reviewKindClass}">
<th class="center">{tr2.cardStatsReviewLogRating()}</th> {row.reviewKind}
<th class="right">{tr2.cardStatsInterval()}</th> </td>
<th class="center hidden-xs">{tr2.cardStatsEase()}</th> <td class="center {row.ratingClass}">{row.rating}</td>
<th class="right">{tr2.cardStatsReviewLogTimeTaken()}</th> <td class="right">{row.interval}</td>
<td class="center hidden-xs">{row.ease}</td>
<td class="right">{row.takenSecs}</td>
</tr> </tr>
{#each revlogRows as row, _index} {/each}
<tr> </table>
<td class="left"><b>{row.date}</b> @ {row.time}</td>
<td class="center hidden-xs {row.reviewKindClass}">
{row.reviewKind}
</td>
<td class="center {row.ratingClass}">{row.rating}</td>
<td class="right">{row.interval}</td>
<td class="center hidden-xs">{row.ease}</td>
<td class="right">{row.takenSecs}</td>
</tr>
{/each}
</table>
</div>
{/if} {/if}
<style> <style>
@ -118,10 +114,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
text-align: center; text-align: center;
} }
.revlog-container {
margin-top: 2em;
}
.revlog-table { .revlog-table {
width: 100%; width: 100%;
border-spacing: 1em 0; border-spacing: 1em 0;

View file

@ -1,3 +1 @@
@import "sass/base"; @import "sass/base";
@import "sass/bootstrap/scss/containers";

View file

@ -3,6 +3,9 @@ Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
--> -->
<script lang="ts"> <script lang="ts">
import Container from "../components/Container.svelte";
import Row from "../components/Row.svelte";
import Col from "../components/Col.svelte";
import MapperRow from "./MapperRow.svelte"; import MapperRow from "./MapperRow.svelte";
import * as tr from "../lib/ftl"; import * as tr from "../lib/ftl";
import { ChangeNotetypeState, MapContext } from "./lib"; import { ChangeNotetypeState, MapContext } from "./lib";
@ -13,26 +16,23 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
let info = state.info; let info = state.info;
let unused: string[]; $: unused = $info.unusedItems(ctx);
let unusedMsg: string; $: unusedMsg =
$: { ctx === MapContext.Field
unused = $info.unusedItems(ctx); ? tr.changeNotetypeWillDiscardContent()
unusedMsg = : tr.changeNotetypeWillDiscardCards();
ctx === MapContext.Field
? tr.changeNotetypeWillDiscardContent()
: tr.changeNotetypeWillDiscardCards();
}
</script> </script>
<div class="container m-1"> <Container --gutter-inline="0.5rem" --gutter-block="0.1rem">
<div class="row"> <Row --cols={2}>
<div class="col"><b>{tr.changeNotetypeCurrent()}</b></div> <Col --col-size={1}><b>{tr.changeNotetypeCurrent()}</b></Col>
<div class="col"><b>{tr.changeNotetypeNew()}</b></div> <Col --col-size={1}><b>{tr.changeNotetypeNew()}</b></Col>
</div> </Row>
{#each $info.mapForContext(ctx) as _, newIndex} {#each $info.mapForContext(ctx) as _, newIndex}
<MapperRow {state} {ctx} {newIndex} /> <MapperRow {state} {ctx} {newIndex} />
{/each} {/each}
</div> </Container>
{#if unused.length > 0} {#if unused.length > 0}
<div class="alert alert-warning" in:slide out:slide> <div class="alert alert-warning" in:slide out:slide>

View file

@ -3,6 +3,8 @@ Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
--> -->
<script lang="ts"> <script lang="ts">
import Row from "../components/Row.svelte";
import Col from "../components/Col.svelte";
import type { ChangeNotetypeState, MapContext } from "./lib"; import type { ChangeNotetypeState, MapContext } from "./lib";
export let state: ChangeNotetypeState; export let state: ChangeNotetypeState;
@ -17,8 +19,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
} }
</script> </script>
<div class="row"> <Row --cols={2}>
<div class="col"> <Col --col-size={1}>
<!-- svelte-ignore a11y-no-onchange --> <!-- svelte-ignore a11y-no-onchange -->
<select <select
value={$info.getOldIndex(ctx, newIndex)} value={$info.getOldIndex(ctx, newIndex)}
@ -29,8 +31,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
<option value={idx}>{name}</option> <option value={idx}>{name}</option>
{/each} {/each}
</select> </select>
</div> </Col>
<div class="col align-self-center"> <Col --col-size={1}>
{$info.getNewName(ctx, newIndex)} {$info.getNewName(ctx, newIndex)}
</div> </Col>
</div> </Row>

View file

@ -5,7 +5,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
<script lang="ts"> <script lang="ts">
import type { ChangeNotetypeState } from "./lib"; import type { ChangeNotetypeState } from "./lib";
import StickyHeader from "../components/StickyHeader.svelte"; import StickyContainer from "../components/StickyContainer.svelte";
import ButtonToolbar from "../components/ButtonToolbar.svelte"; import ButtonToolbar from "../components/ButtonToolbar.svelte";
import Item from "../components/Item.svelte"; import Item from "../components/Item.svelte";
import ButtonGroup from "../components/ButtonGroup.svelte"; import ButtonGroup from "../components/ButtonGroup.svelte";
@ -25,7 +25,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
} }
</script> </script>
<StickyHeader> <StickyContainer --gutter-block="0.1rem" --sticky-borders="0 0 1px">
<ButtonToolbar class="justify-content-between" size={2.3} wrap={false}> <ButtonToolbar class="justify-content-between" size={2.3} wrap={false}>
<Item> <Item>
<ButtonGroup class="flex-grow-1"> <ButtonGroup class="flex-grow-1">
@ -48,4 +48,4 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
<SaveButton {state} /> <SaveButton {state} />
</Item> </Item>
</ButtonToolbar> </ButtonToolbar>
</StickyHeader> </StickyContainer>

View file

@ -39,6 +39,7 @@ svelte_check(
"//sass:base_lib", "//sass:base_lib",
"//sass:button_mixins_lib", "//sass:button_mixins_lib",
"//sass:scrollbar_lib", "//sass:scrollbar_lib",
"//sass:breakpoints_lib",
"//sass/bootstrap", "//sass/bootstrap",
"@npm//@types/bootstrap", "@npm//@types/bootstrap",
"//ts/sveltelib:sveltelib_pkg", "//ts/sveltelib:sveltelib_pkg",

View file

@ -111,7 +111,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
<div <div
bind:this={buttonGroupRef} bind:this={buttonGroupRef}
{id} {id}
class="btn-group {className}" class="button-group btn-group {className}"
{style} {style}
dir="ltr" dir="ltr"
role="group" role="group"
@ -125,9 +125,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
</div> </div>
<style lang="scss"> <style lang="scss">
div { .button-group {
display: flex; display: flex;
flex-direction: row; flex-flow: row var(--buttons-wrap);
flex-wrap: var(--buttons-wrap);
} }
</style> </style>

View file

@ -119,7 +119,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
.button-toolbar { .button-toolbar {
flex-wrap: var(--buttons-wrap); flex-wrap: var(--buttons-wrap);
padding: 0.15rem 0; padding-left: 0.15rem;
> :global(*) > :global(*) { > :global(*) > :global(*) {
/* TODO replace with gap once available */ /* TODO replace with gap once available */

52
ts/components/Col.svelte Normal file
View file

@ -0,0 +1,52 @@
<!--
Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
<script lang="ts">
import type { Breakpoint } from "./types";
/* flex-basis: 100% if viewport < breakpoint otherwise
* as specified by --cols and --col-size */
export let breakpoint: Breakpoint = "xs";
</script>
<div
class="col"
class:col-xs={breakpoint === "xs"}
class:col-sm={breakpoint === "sm"}
class:col-md={breakpoint === "md"}
class:col-lg={breakpoint === "lg"}
class:col-xl={breakpoint === "xl"}
class:col-xxl={breakpoint === "xxl"}
>
<slot />
</div>
<style lang="scss">
@use "sass/breakpoints" as bp;
.col {
display: flex;
flex-flow: row nowrap;
align-items: var(--col-align, flex-start);
justify-content: var(--col-justify, flex-start);
padding: 0 var(--gutter-inline, 0);
flex: 1 0 100%;
}
$calc: calc(100% / var(--cols, 1) * var(--col-size, 1));
@include bp.with-breakpoints(
"col",
(
"flex-basis": (
"xs": $calc,
"sm": $calc,
"md": $calc,
"lg": $calc,
"xl": $calc,
"xxl": $calc
)
)
);
</style>

View file

@ -4,16 +4,62 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
--> -->
<script lang="ts"> <script lang="ts">
import Section from "./Section.svelte"; import Section from "./Section.svelte";
import type { Breakpoint } from "./types";
export let id: string | undefined = undefined; export let id: string | undefined = undefined;
let className: string = ""; let className: string = "";
export { className as class }; export { className as class };
/* width: 100% if viewport < breakpoint otherwise with gutters */
export let breakpoint: Breakpoint | "fluid" = "fluid";
export let api: Record<string, never> | undefined = undefined; export let api: Record<string, never> | undefined = undefined;
</script> </script>
<div {id} class="container {className}"> <div
{id}
class="container {className}"
class:container-xs={breakpoint === "xs"}
class:container-sm={breakpoint === "sm"}
class:container-md={breakpoint === "md"}
class:container-lg={breakpoint === "lg"}
class:container-xl={breakpoint === "xl"}
class:container-xxl={breakpoint === "xxl"}
class:container-fluid={breakpoint === "fluid"}
>
<Section {api}> <Section {api}>
<slot /> <slot />
</Section> </Section>
</div> </div>
<style lang="scss">
@use "sass/breakpoints";
.container {
display: flex;
flex-direction: var(--container-direction, column);
padding: var(--gutter-block, 0) var(--gutter-inline, 0);
margin: 0 auto;
&.container-fluid {
width: 100%;
height: 100%;
margin: 0;
}
}
@include breakpoints.with-breakpoints-upto(
"container",
(
"max-width": (
"xs": 360px,
"sm": 540px,
"md": 720px,
"lg": 960px,
"xl": 1140px,
"xxl": 1320px,
),
)
);
</style>

26
ts/components/Row.svelte Normal file
View file

@ -0,0 +1,26 @@
<!--
Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
<script lang="ts">
import Item from "../components/Item.svelte";
export let id: string | undefined = undefined;
let className: string = "";
export { className as class };
</script>
<Item {id}>
<div class="row {className}">
<slot />
</div>
</Item>
<style lang="scss">
.row {
display: flex;
flex-flow: row wrap;
align-content: stretch;
padding: var(--gutter-block, 0) 0;
}
</style>

View file

@ -3,20 +3,29 @@ Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
--> -->
<script lang="ts"> <script lang="ts">
import Container from "./Container.svelte";
import type { Breakpoint } from "./types";
export let id: string | undefined = undefined; export let id: string | undefined = undefined;
let className: string = ""; let className: string = "";
export { className as class }; export { className as class };
export let height: number = 0; export let height: number = 0;
export let breakpoint: Breakpoint | "fluid" = "fluid";
export let api: Record<string, never> | undefined = undefined;
</script> </script>
<footer {id} bind:offsetHeight={height} class="sticky-footer {className}"> <div {id} bind:offsetHeight={height} class="sticky-container {className}">
<slot /> <Container {breakpoint} {api}>
</footer> <slot />
</Container>
</div>
<style lang="scss"> <style lang="scss">
.sticky-footer { .sticky-container {
position: sticky; position: sticky;
top: 0;
bottom: 0;
left: 0; left: 0;
right: 0; right: 0;
z-index: 10; z-index: 10;
@ -24,10 +33,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
background: var(--window-bg); background: var(--window-bg);
border-style: solid; border-style: solid;
border-color: var(--medium-border); border-color: var(--medium-border);
border-width: 0; border-width: var(--sticky-borders, 0);
padding: 0 3px;
bottom: 0;
border-top-width: 1px;
} }
</style> </style>

View file

@ -1,31 +0,0 @@
<!--
Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
<script lang="ts">
export let id: string | undefined = undefined;
let className: string = "";
export { className as class };
</script>
<header {id} class="sticky-header container-fluid {className}">
<slot />
</header>
<style lang="scss">
.sticky-header {
position: sticky;
left: 0;
right: 0;
z-index: 10;
background: var(--window-bg);
border-style: solid;
border-color: var(--medium-border);
border-width: 0;
padding: 0 3px;
top: 0;
border-bottom-width: 1px;
}
</style>

View file

@ -1,4 +1,5 @@
// Copyright: Ankitects Pty Ltd and contributors // Copyright: Ankitects Pty Ltd and contributors
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
export type Size = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12; export type Size = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
export type Breakpoint = "xs" | "sm" | "md" | "lg" | "xl"; export type Breakpoint = "xs" | "sm" | "md" | "lg" | "xl" | "xxl";

View file

@ -5,7 +5,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
<script lang="ts"> <script lang="ts">
import * as tr from "../lib/ftl"; import * as tr from "../lib/ftl";
import TitledContainer from "./TitledContainer.svelte"; import TitledContainer from "./TitledContainer.svelte";
import Item from "../components/Item.svelte";
import SpinBoxRow from "./SpinBoxRow.svelte"; import SpinBoxRow from "./SpinBoxRow.svelte";
import SpinBoxFloatRow from "./SpinBoxFloatRow.svelte"; import SpinBoxFloatRow from "./SpinBoxFloatRow.svelte";
import type { DeckOptionsState } from "./lib"; import type { DeckOptionsState } from "./lib";
@ -20,80 +19,66 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
</script> </script>
<TitledContainer title={tr.deckConfigAdvancedTitle()} {api}> <TitledContainer title={tr.deckConfigAdvancedTitle()} {api}>
<Item> <SpinBoxRow
<SpinBoxRow bind:value={$config.maximumReviewInterval}
bind:value={$config.maximumReviewInterval} defaultValue={defaults.maximumReviewInterval}
defaultValue={defaults.maximumReviewInterval} min={1}
min={1} max={365 * 100}
max={365 * 100} markdownTooltip={tr.deckConfigMaximumIntervalTooltip()}
markdownTooltip={tr.deckConfigMaximumIntervalTooltip()} >
> {tr.schedulingMaximumInterval()}
{tr.schedulingMaximumInterval()} </SpinBoxRow>
</SpinBoxRow>
</Item>
<Item> <SpinBoxFloatRow
<SpinBoxFloatRow bind:value={$config.initialEase}
bind:value={$config.initialEase} defaultValue={defaults.initialEase}
defaultValue={defaults.initialEase} min={1.31}
min={1.31} max={5}
max={5} markdownTooltip={tr.deckConfigStartingEaseTooltip()}
markdownTooltip={tr.deckConfigStartingEaseTooltip()} >
> {tr.schedulingStartingEase()}
{tr.schedulingStartingEase()} </SpinBoxFloatRow>
</SpinBoxFloatRow>
</Item>
<Item> <SpinBoxFloatRow
<SpinBoxFloatRow bind:value={$config.easyMultiplier}
bind:value={$config.easyMultiplier} defaultValue={defaults.easyMultiplier}
defaultValue={defaults.easyMultiplier} min={1}
min={1} max={3}
max={3} markdownTooltip={tr.deckConfigEasyBonusTooltip()}
markdownTooltip={tr.deckConfigEasyBonusTooltip()} >
> {tr.schedulingEasyBonus()}
{tr.schedulingEasyBonus()} </SpinBoxFloatRow>
</SpinBoxFloatRow>
</Item>
<Item> <SpinBoxFloatRow
<SpinBoxFloatRow bind:value={$config.intervalMultiplier}
bind:value={$config.intervalMultiplier} defaultValue={defaults.intervalMultiplier}
defaultValue={defaults.intervalMultiplier} min={0.5}
min={0.5} max={2}
max={2} markdownTooltip={tr.deckConfigIntervalModifierTooltip()}
markdownTooltip={tr.deckConfigIntervalModifierTooltip()} >
> {tr.schedulingIntervalModifier()}
{tr.schedulingIntervalModifier()} </SpinBoxFloatRow>
</SpinBoxFloatRow>
</Item>
<Item> <SpinBoxFloatRow
<SpinBoxFloatRow bind:value={$config.hardMultiplier}
bind:value={$config.hardMultiplier} defaultValue={defaults.hardMultiplier}
defaultValue={defaults.hardMultiplier} min={0.5}
min={0.5} max={1.3}
max={1.3} markdownTooltip={tr.deckConfigHardIntervalTooltip()}
markdownTooltip={tr.deckConfigHardIntervalTooltip()} >
> {tr.schedulingHardInterval()}
{tr.schedulingHardInterval()} </SpinBoxFloatRow>
</SpinBoxFloatRow>
</Item>
<Item> <SpinBoxFloatRow
<SpinBoxFloatRow bind:value={$config.lapseMultiplier}
bind:value={$config.lapseMultiplier} defaultValue={defaults.lapseMultiplier}
defaultValue={defaults.lapseMultiplier} max={1}
max={1} markdownTooltip={tr.deckConfigNewIntervalTooltip()}
markdownTooltip={tr.deckConfigNewIntervalTooltip()} >
> {tr.schedulingNewInterval()}
{tr.schedulingNewInterval()} </SpinBoxFloatRow>
</SpinBoxFloatRow>
</Item>
{#if state.v3Scheduler} {#if state.v3Scheduler}
<Item> <CardStateCustomizer bind:value={$cardStateCustomizer} />
<CardStateCustomizer bind:value={$cardStateCustomizer} />
</Item>
{/if} {/if}
</TitledContainer> </TitledContainer>

View file

@ -5,7 +5,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
<script lang="ts"> <script lang="ts">
import * as tr from "../lib/ftl"; import * as tr from "../lib/ftl";
import TitledContainer from "./TitledContainer.svelte"; import TitledContainer from "./TitledContainer.svelte";
import Item from "../components/Item.svelte";
import SwitchRow from "./SwitchRow.svelte"; import SwitchRow from "./SwitchRow.svelte";
import type { DeckOptionsState } from "./lib"; import type { DeckOptionsState } from "./lib";
@ -17,22 +16,18 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
</script> </script>
<TitledContainer title={tr.deckConfigAudioTitle()} {api}> <TitledContainer title={tr.deckConfigAudioTitle()} {api}>
<Item> <SwitchRow
<SwitchRow bind:value={$config.disableAutoplay}
bind:value={$config.disableAutoplay} defaultValue={defaults.disableAutoplay}
defaultValue={defaults.disableAutoplay} >
> {tr.deckConfigDisableAutoplay()}
{tr.deckConfigDisableAutoplay()} </SwitchRow>
</SwitchRow>
</Item>
<Item> <SwitchRow
<SwitchRow bind:value={$config.skipQuestionWhenReplayingAnswer}
bind:value={$config.skipQuestionWhenReplayingAnswer} defaultValue={defaults.skipQuestionWhenReplayingAnswer}
defaultValue={defaults.skipQuestionWhenReplayingAnswer} markdownTooltip={tr.deckConfigAlwaysIncludeQuestionAudioTooltip()}
markdownTooltip={tr.deckConfigAlwaysIncludeQuestionAudioTooltip()} >
> {tr.deckConfigSkipQuestionWhenReplaying()}
{tr.deckConfigSkipQuestionWhenReplaying()} </SwitchRow>
</SwitchRow>
</Item>
</TitledContainer> </TitledContainer>

View file

@ -14,6 +14,7 @@ compile_sass(
deps = [ deps = [
"//sass:base_lib", "//sass:base_lib",
"//sass:scrollbar_lib", "//sass:scrollbar_lib",
"//sass:breakpoints_lib",
"//sass/bootstrap", "//sass/bootstrap",
], ],
) )
@ -77,6 +78,7 @@ svelte_check(
]) + [ ]) + [
"//sass:button_mixins_lib", "//sass:button_mixins_lib",
"//sass:night_mode_lib", "//sass:night_mode_lib",
"//sass:breakpoints_lib",
"//sass/bootstrap", "//sass/bootstrap",
"@npm//@types/bootstrap", "@npm//@types/bootstrap",
"@npm//@types/lodash-es", "@npm//@types/lodash-es",

View file

@ -4,7 +4,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
--> -->
<script lang="ts"> <script lang="ts">
import TitledContainer from "./TitledContainer.svelte"; import TitledContainer from "./TitledContainer.svelte";
import Item from "../components/Item.svelte";
import SwitchRow from "./SwitchRow.svelte"; import SwitchRow from "./SwitchRow.svelte";
import * as tr from "../lib/ftl"; import * as tr from "../lib/ftl";
import type { DeckOptionsState } from "./lib"; import type { DeckOptionsState } from "./lib";
@ -17,23 +16,19 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
</script> </script>
<TitledContainer title={tr.deckConfigBuryTitle()} {api}> <TitledContainer title={tr.deckConfigBuryTitle()} {api}>
<Item> <SwitchRow
<SwitchRow bind:value={$config.buryNew}
bind:value={$config.buryNew} defaultValue={defaults.buryNew}
defaultValue={defaults.buryNew} markdownTooltip={tr.deckConfigBuryTooltip()}
markdownTooltip={tr.deckConfigBuryTooltip()} >
> {tr.deckConfigBuryNewSiblings()}
{tr.deckConfigBuryNewSiblings()} </SwitchRow>
</SwitchRow>
</Item>
<Item> <SwitchRow
<SwitchRow bind:value={$config.buryReviews}
bind:value={$config.buryReviews} defaultValue={defaults.buryReviews}
defaultValue={defaults.buryReviews} markdownTooltip={tr.deckConfigBuryTooltip()}
markdownTooltip={tr.deckConfigBuryTooltip()} >
> {tr.deckConfigBuryReviewSiblings()}
{tr.deckConfigBuryReviewSiblings()} </SwitchRow>
</SwitchRow>
</Item>
</TitledContainer> </TitledContainer>

View file

@ -4,10 +4,10 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
--> -->
<script lang="ts"> <script lang="ts">
import * as tr from "../lib/ftl"; import * as tr from "../lib/ftl";
import Row from "../components/Row.svelte";
import Col from "../components/Col.svelte";
import TooltipLabel from "./TooltipLabel.svelte"; import TooltipLabel from "./TooltipLabel.svelte";
import RevertButton from "./RevertButton.svelte"; import RevertButton from "./RevertButton.svelte";
import Row from "./Row.svelte";
import Col from "./Col.svelte";
export let value: string; export let value: string;
</script> </script>

View file

@ -1,25 +0,0 @@
<!--
Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
<script lang="ts">
import Row from "./Row.svelte";
import Col from "./Col.svelte";
import TooltipLabel from "./TooltipLabel.svelte";
import CheckBox from "./CheckBox.svelte";
import RevertButton from "./RevertButton.svelte";
export let value: boolean;
export let defaultValue: boolean;
export let markdownTooltip: string | undefined = undefined;
</script>
<Row>
<Col>
<RevertButton bind:value {defaultValue} />
<CheckBox bind:value
>{#if markdownTooltip}<TooltipLabel {markdownTooltip}><slot /></TooltipLabel
>{:else}<slot />{/if}</CheckBox
>
</Col>
</Row>

View file

@ -1,22 +0,0 @@
<!--
Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
<script lang="ts">
import type { Breakpoint, Size } from "./col";
export let breakpoint: Breakpoint | undefined = undefined;
export let size: Size | undefined = undefined;
export let grow = true;
let colClass: string;
$: {
const breakpointComponent = breakpoint ? `-${breakpoint}` : "";
const sizeComponent = size ? `-${size}` : "";
colClass = "col" + breakpointComponent + sizeComponent;
}
</script>
<div class={`${colClass} d-flex align-items-center`} class:flex-grow-0={!grow}>
<slot />
</div>

View file

@ -10,12 +10,11 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import type Modal from "bootstrap/js/dist/modal"; import type Modal from "bootstrap/js/dist/modal";
import TextInputModal from "./TextInputModal.svelte"; import TextInputModal from "./TextInputModal.svelte";
import StickyHeader from "../components/StickyHeader.svelte"; import StickyContainer from "../components/StickyContainer.svelte";
import ButtonToolbar from "../components/ButtonToolbar.svelte"; import ButtonToolbar from "../components/ButtonToolbar.svelte";
import Item from "../components/Item.svelte"; import Item from "../components/Item.svelte";
import ButtonGroup from "../components/ButtonGroup.svelte"; import ButtonGroup from "../components/ButtonGroup.svelte";
import ButtonGroupItem from "../components/ButtonGroupItem.svelte"; import ButtonGroupItem from "../components/ButtonGroupItem.svelte";
import Container from "../components/Container.svelte";
import SelectButton from "../components/SelectButton.svelte"; import SelectButton from "../components/SelectButton.svelte";
import SelectOption from "../components/SelectOption.svelte"; import SelectOption from "../components/SelectOption.svelte";
@ -88,34 +87,32 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
bind:modalKey bind:modalKey
/> />
<StickyHeader class="g-1"> <StickyContainer --gutter-block="0.1rem" --sticky-borders="0 0 1px" breakpoint="sm">
<Container> <ButtonToolbar class="justify-content-between" size={2.3} wrap={false}>
<ButtonToolbar class="justify-content-between" size={2.3} wrap={false}> <Item>
<Item> <ButtonGroup class="flex-grow-1">
<ButtonGroup class="flex-grow-1"> <ButtonGroupItem>
<ButtonGroupItem> <SelectButton class="flex-grow-1" on:change={blur}>
<SelectButton class="flex-grow-1" on:change={blur}> {#each $configList as entry}
{#each $configList as entry} <SelectOption
<SelectOption value={String(entry.idx)}
value={String(entry.idx)} selected={entry.current}
selected={entry.current} >
> {configLabel(entry)}
{configLabel(entry)} </SelectOption>
</SelectOption> {/each}
{/each} </SelectButton>
</SelectButton> </ButtonGroupItem>
</ButtonGroupItem> </ButtonGroup>
</ButtonGroup> </Item>
</Item>
<Item> <Item>
<SaveButton <SaveButton
{state} {state}
on:add={promptToAdd} on:add={promptToAdd}
on:clone={promptToClone} on:clone={promptToClone}
on:rename={promptToRename} on:rename={promptToRename}
/> />
</Item> </Item>
</ButtonToolbar> </ButtonToolbar>
</Container> </StickyContainer>
</StickyHeader>

View file

@ -41,27 +41,27 @@
</script> </script>
<TitledContainer title={tr.deckConfigDailyLimits()} {api}> <TitledContainer title={tr.deckConfigDailyLimits()} {api}>
<Item> <SpinBoxRow
<SpinBoxRow bind:value={$config.newPerDay}
bind:value={$config.newPerDay} defaultValue={defaults.newPerDay}
defaultValue={defaults.newPerDay} markdownTooltip={tr.deckConfigNewLimitTooltip() + v3Extra}
markdownTooltip={tr.deckConfigNewLimitTooltip() + v3Extra} >
> {tr.schedulingNewCardsday()}
{tr.schedulingNewCardsday()} </SpinBoxRow>
</SpinBoxRow>
<Item>
<Warning warning={newCardsGreaterThanParent} /> <Warning warning={newCardsGreaterThanParent} />
</Item> </Item>
<Item> <SpinBoxRow
<SpinBoxRow bind:value={$config.reviewsPerDay}
bind:value={$config.reviewsPerDay} defaultValue={defaults.reviewsPerDay}
defaultValue={defaults.reviewsPerDay} markdownTooltip={tr.deckConfigReviewLimitTooltip() + v3Extra}
markdownTooltip={tr.deckConfigReviewLimitTooltip() + v3Extra} >
> {tr.schedulingMaximumReviewsday()}
{tr.schedulingMaximumReviewsday()} </SpinBoxRow>
</SpinBoxRow>
<Item>
<Warning warning={reviewsTooLow} /> <Warning warning={reviewsTooLow} />
</Item> </Item>
</TitledContainer> </TitledContainer>

View file

@ -5,7 +5,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
<script lang="ts"> <script lang="ts">
import ConfigSelector from "./ConfigSelector.svelte"; import ConfigSelector from "./ConfigSelector.svelte";
import Container from "../components/Container.svelte"; import Container from "../components/Container.svelte";
import Item from "../components/Item.svelte"; import Row from "../components/Row.svelte";
import DailyLimits from "./DailyLimits.svelte"; import DailyLimits from "./DailyLimits.svelte";
import DisplayOrder from "./DisplayOrder.svelte"; import DisplayOrder from "./DisplayOrder.svelte";
import NewOptions from "./NewOptions.svelte"; import NewOptions from "./NewOptions.svelte";
@ -57,57 +57,77 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
<ConfigSelector {state} /> <ConfigSelector {state} />
<div class="multi-column"> <div class="deck-options-page">
<Container api={options} class="g-1 mb-3 mt-3"> <Container
<Item> breakpoint="sm"
--gutter-inline="0.25rem"
--gutter-block="0.5rem"
class="container-columns"
api={options}
>
<Row class="row-columns">
<DailyLimits {state} api={dailyLimits} /> <DailyLimits {state} api={dailyLimits} />
</Item> </Row>
<Item> <Row class="row-columns">
<NewOptions {state} api={newOptions} /> <NewOptions {state} api={newOptions} />
</Item> </Row>
<Item> <Row class="row-columns">
<LapseOptions {state} api={lapseOptions} /> <LapseOptions {state} api={lapseOptions} />
</Item> </Row>
{#if state.v3Scheduler} {#if state.v3Scheduler}
<Item> <Row class="row-columns">
<DisplayOrder {state} api={displayOrder} /> <DisplayOrder {state} api={displayOrder} />
</Item> </Row>
{/if} {/if}
<Item> <Row class="row-columns">
<TimerOptions {state} api={timerOptions} /> <TimerOptions {state} api={timerOptions} />
</Item> </Row>
<Item> <Row class="row-columns">
<BuryOptions {state} api={buryOptions} /> <BuryOptions {state} api={buryOptions} />
</Item> </Row>
<Item> <Row class="row-columns">
<AudioOptions {state} api={audioOptions} /> <AudioOptions {state} api={audioOptions} />
</Item> </Row>
<Item> <Row class="row-columns">
<Addons {state} api={addonOptions} /> <Addons {state} api={addonOptions} />
</Item> </Row>
<Item> <Row class="row-columns">
<AdvancedOptions {state} api={advancedOptions} /> <AdvancedOptions {state} api={advancedOptions} />
</Item> </Row>
</Container> </Container>
</div> </div>
<style lang="scss"> <style lang="scss">
.multi-column :global(.container) { @use "sass/breakpoints" as bp;
column-count: 2;
column-gap: 5em;
}
@media (max-width: 1000px) { .deck-options-page {
.multi-column :global(.container) { overflow-x: hidden;
column-count: 1;
@include bp.with-breakpoint("lg") {
:global(.container) {
display: block;
}
:global(.container-columns) {
column-count: 2;
column-gap: 5em;
:global(.container) {
break-inside: avoid;
}
}
:global(.row-columns) {
display: block;
}
} }
} }
</style> </style>

View file

@ -16,7 +16,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
bind:value bind:value
class:nightMode class:nightMode
class:visible-down-arrow={nightMode} class:visible-down-arrow={nightMode}
class="form-select" class="enum-selector form-select"
> >
{#each choices as choice, idx} {#each choices as choice, idx}
<option value={idx}>{choice}</option> <option value={idx}>{choice}</option>
@ -31,6 +31,11 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
@include nightmode.input; @include nightmode.input;
} }
.enum-selector {
/* overwrite Bootstrap */
padding: 0.2rem 0.75rem;
}
.visible-down-arrow { .visible-down-arrow {
/* override the default down arrow */ /* override the default down arrow */
background-image: button.down-arrow(white); background-image: button.down-arrow(white);

View file

@ -3,10 +3,10 @@
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
--> -->
<script lang="ts"> <script lang="ts">
import type { Breakpoint } from "./col"; import type { Breakpoint } from "../components/types";
import Row from "./Row.svelte"; import Row from "../components/Row.svelte";
import Col from "./Col.svelte"; import Col from "../components/Col.svelte";
import TooltipLabel from "./TooltipLabel.svelte"; import TooltipLabel from "./TooltipLabel.svelte";
import EnumSelector from "./EnumSelector.svelte"; import EnumSelector from "./EnumSelector.svelte";
import RevertButton from "./RevertButton.svelte"; import RevertButton from "./RevertButton.svelte";
@ -18,11 +18,11 @@
export let markdownTooltip: string; export let markdownTooltip: string;
</script> </script>
<Row> <Row --cols={12}>
<Col size={7}> <Col --col-size={7} {breakpoint}>
<TooltipLabel {markdownTooltip}><slot /></TooltipLabel> <TooltipLabel {markdownTooltip}><slot /></TooltipLabel>
</Col> </Col>
<Col {breakpoint} size={5}> <Col --col-size={5} {breakpoint}>
<EnumSelector bind:value {choices} /> <EnumSelector bind:value {choices} />
<RevertButton bind:value {defaultValue} /> <RevertButton bind:value {defaultValue} />
</Col> </Col>

View file

@ -18,3 +18,9 @@
</script> </script>
<label bind:this={spanRef} for={forId}><slot /></label> <label bind:this={spanRef} for={forId}><slot /></label>
<style lang="scss">
label {
display: inline;
}
</style>

View file

@ -33,49 +33,43 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
</script> </script>
<TitledContainer title={tr.schedulingLapses()} {api}> <TitledContainer title={tr.schedulingLapses()} {api}>
<Item> <StepsInputRow
<StepsInputRow bind:value={$config.relearnSteps}
bind:value={$config.relearnSteps} defaultValue={defaults.relearnSteps}
defaultValue={defaults.relearnSteps} markdownTooltip={tr.deckConfigRelearningStepsTooltip()}
markdownTooltip={tr.deckConfigRelearningStepsTooltip()} >
> {tr.deckConfigRelearningSteps()}
{tr.deckConfigRelearningSteps()} </StepsInputRow>
</StepsInputRow>
</Item> <SpinBoxRow
bind:value={$config.minimumLapseInterval}
defaultValue={defaults.minimumLapseInterval}
min={1}
markdownTooltip={tr.deckConfigMinimumIntervalTooltip()}
>
{tr.schedulingMinimumInterval()}
</SpinBoxRow>
<Item> <Item>
<SpinBoxRow
bind:value={$config.minimumLapseInterval}
defaultValue={defaults.minimumLapseInterval}
min={1}
markdownTooltip={tr.deckConfigMinimumIntervalTooltip()}
>
{tr.schedulingMinimumInterval()}
</SpinBoxRow>
<Warning warning={stepsExceedMinimumInterval} /> <Warning warning={stepsExceedMinimumInterval} />
</Item> </Item>
<Item> <SpinBoxRow
<SpinBoxRow bind:value={$config.leechThreshold}
bind:value={$config.leechThreshold} defaultValue={defaults.leechThreshold}
defaultValue={defaults.leechThreshold} min={1}
min={1} markdownTooltip={tr.deckConfigLeechThresholdTooltip()}
markdownTooltip={tr.deckConfigLeechThresholdTooltip()} >
> {tr.schedulingLeechThreshold()}
{tr.schedulingLeechThreshold()} </SpinBoxRow>
</SpinBoxRow>
</Item>
<Item> <EnumSelectorRow
<EnumSelectorRow bind:value={$config.leechAction}
bind:value={$config.leechAction} defaultValue={defaults.leechAction}
defaultValue={defaults.leechAction} choices={leechChoices}
choices={leechChoices} breakpoint="sm"
breakpoint="sm" markdownTooltip={tr.deckConfigLeechActionTooltip()}
markdownTooltip={tr.deckConfigLeechActionTooltip()} >
> {tr.schedulingLeechAction()}
{tr.schedulingLeechAction()} </EnumSelectorRow>
</EnumSelectorRow>
</Item>
</TitledContainer> </TitledContainer>

View file

@ -4,10 +4,10 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
--> -->
<script lang="ts"> <script lang="ts">
import TitledContainer from "./TitledContainer.svelte"; import TitledContainer from "./TitledContainer.svelte";
import Item from "../components/Item.svelte";
import StepsInputRow from "./StepsInputRow.svelte"; import StepsInputRow from "./StepsInputRow.svelte";
import SpinBoxRow from "./SpinBoxRow.svelte"; import SpinBoxRow from "./SpinBoxRow.svelte";
import EnumSelectorRow from "./EnumSelectorRow.svelte"; import EnumSelectorRow from "./EnumSelectorRow.svelte";
import Item from "../components/Item.svelte";
import Warning from "./Warning.svelte"; import Warning from "./Warning.svelte";
import type { DeckOptionsState } from "./lib"; import type { DeckOptionsState } from "./lib";
@ -42,49 +42,45 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
</script> </script>
<TitledContainer title={tr.schedulingNewCards()} {api}> <TitledContainer title={tr.schedulingNewCards()} {api}>
<Item> <StepsInputRow
<StepsInputRow bind:value={$config.learnSteps}
bind:value={$config.learnSteps} defaultValue={defaults.learnSteps}
defaultValue={defaults.learnSteps} markdownTooltip={tr.deckConfigLearningStepsTooltip()}
markdownTooltip={tr.deckConfigLearningStepsTooltip()} >
> {tr.deckConfigLearningSteps()}
{tr.deckConfigLearningSteps()} </StepsInputRow>
</StepsInputRow>
</Item> <SpinBoxRow
bind:value={$config.graduatingIntervalGood}
defaultValue={defaults.graduatingIntervalGood}
markdownTooltip={tr.deckConfigGraduatingIntervalTooltip()}
>
{tr.schedulingGraduatingInterval()}
</SpinBoxRow>
<Item> <Item>
<SpinBoxRow
bind:value={$config.graduatingIntervalGood}
defaultValue={defaults.graduatingIntervalGood}
markdownTooltip={tr.deckConfigGraduatingIntervalTooltip()}
>
{tr.schedulingGraduatingInterval()}
</SpinBoxRow>
<Warning warning={stepsExceedGraduatingInterval} /> <Warning warning={stepsExceedGraduatingInterval} />
</Item> </Item>
<Item> <SpinBoxRow
<SpinBoxRow bind:value={$config.graduatingIntervalEasy}
bind:value={$config.graduatingIntervalEasy} defaultValue={defaults.graduatingIntervalEasy}
defaultValue={defaults.graduatingIntervalEasy} markdownTooltip={tr.deckConfigEasyIntervalTooltip()}
markdownTooltip={tr.deckConfigEasyIntervalTooltip()} >
> {tr.schedulingEasyInterval()}
{tr.schedulingEasyInterval()} </SpinBoxRow>
</SpinBoxRow>
<Item>
<Warning warning={goodExceedsEasy} /> <Warning warning={goodExceedsEasy} />
</Item> </Item>
<Item> <EnumSelectorRow
<EnumSelectorRow bind:value={$config.newCardInsertOrder}
bind:value={$config.newCardInsertOrder} defaultValue={defaults.newCardInsertOrder}
defaultValue={defaults.newCardInsertOrder} choices={newInsertOrderChoices}
choices={newInsertOrderChoices} breakpoint={"md"}
breakpoint={"md"} markdownTooltip={tr.deckConfigNewInsertionOrderTooltip()}
markdownTooltip={tr.deckConfigNewInsertionOrderTooltip()} >
> {tr.deckConfigNewInsertionOrder()}
{tr.deckConfigNewInsertionOrder()} </EnumSelectorRow>
</EnumSelectorRow>
</Item>
</TitledContainer> </TitledContainer>

View file

@ -1,11 +0,0 @@
<!--
Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
<script lang="ts">
export let id: string | undefined = undefined;
</script>
<div {id} class="row gx-0 gy-2 mt-0">
<slot />
</div>

View file

@ -28,7 +28,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
{min} {min}
{max} {max}
bind:value bind:value
class="form-control" class="spin-box form-control"
class:nightMode class:nightMode
on:blur={checkMinMax} on:blur={checkMinMax}
/> />
@ -36,6 +36,11 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
<style lang="scss"> <style lang="scss">
@use "sass/night-mode" as nightmode; @use "sass/night-mode" as nightmode;
.spin-box {
/* overwrite Bootstrap */
padding: 0.2rem 0.75rem;
}
.nightMode { .nightMode {
@include nightmode.input; @include nightmode.input;
} }

View file

@ -3,8 +3,8 @@
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
--> -->
<script lang="ts"> <script lang="ts">
import Row from "./Row.svelte"; import Row from "../components/Row.svelte";
import Col from "./Col.svelte"; import Col from "../components/Col.svelte";
import TooltipLabel from "./TooltipLabel.svelte"; import TooltipLabel from "./TooltipLabel.svelte";
import SpinBoxFloat from "./SpinBoxFloat.svelte"; import SpinBoxFloat from "./SpinBoxFloat.svelte";
import RevertButton from "./RevertButton.svelte"; import RevertButton from "./RevertButton.svelte";
@ -16,11 +16,11 @@
export let markdownTooltip: string; export let markdownTooltip: string;
</script> </script>
<Row> <Row --cols={12}>
<Col size={7}> <Col --col-size={7} breakpoint="sm">
<TooltipLabel {markdownTooltip}><slot /></TooltipLabel> <TooltipLabel {markdownTooltip}><slot /></TooltipLabel>
</Col> </Col>
<Col size={5}> <Col --col-size={5} breakpoint="sm">
<SpinBoxFloat bind:value {min} {max} /> <SpinBoxFloat bind:value {min} {max} />
<RevertButton bind:value {defaultValue} /> <RevertButton bind:value {defaultValue} />
</Col> </Col>

View file

@ -3,8 +3,8 @@
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
--> -->
<script lang="ts"> <script lang="ts">
import Row from "./Row.svelte"; import Row from "../components/Row.svelte";
import Col from "./Col.svelte"; import Col from "../components/Col.svelte";
import TooltipLabel from "./TooltipLabel.svelte"; import TooltipLabel from "./TooltipLabel.svelte";
import SpinBox from "./SpinBox.svelte"; import SpinBox from "./SpinBox.svelte";
import RevertButton from "./RevertButton.svelte"; import RevertButton from "./RevertButton.svelte";
@ -16,11 +16,11 @@
export let markdownTooltip: string; export let markdownTooltip: string;
</script> </script>
<Row> <Row --cols={12}>
<Col size={7}> <Col --col-size={7} breakpoint="sm">
<TooltipLabel {markdownTooltip}><slot /></TooltipLabel> <TooltipLabel {markdownTooltip}><slot /></TooltipLabel>
</Col> </Col>
<Col size={5}> <Col --col-size={5} breakpoint="sm">
<SpinBox bind:value {min} {max} /> <SpinBox bind:value {min} {max} />
<RevertButton bind:value {defaultValue} /> <RevertButton bind:value {defaultValue} />
</Col> </Col>

View file

@ -3,8 +3,8 @@
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
--> -->
<script lang="ts"> <script lang="ts">
import Row from "./Row.svelte"; import Row from "../components/Row.svelte";
import Col from "./Col.svelte"; import Col from "../components/Col.svelte";
import TooltipLabel from "./TooltipLabel.svelte"; import TooltipLabel from "./TooltipLabel.svelte";
import StepsInput from "./StepsInput.svelte"; import StepsInput from "./StepsInput.svelte";
import RevertButton from "./RevertButton.svelte"; import RevertButton from "./RevertButton.svelte";
@ -14,11 +14,11 @@
export let markdownTooltip: string; export let markdownTooltip: string;
</script> </script>
<Row> <Row --cols={12}>
<Col size={7}> <Col --col-size={7} breakpoint="sm">
<TooltipLabel {markdownTooltip}><slot /></TooltipLabel> <TooltipLabel {markdownTooltip}><slot /></TooltipLabel>
</Col> </Col>
<Col size={5}> <Col --col-size={5} breakpoint="sm">
<StepsInput bind:value /> <StepsInput bind:value />
<RevertButton bind:value {defaultValue} /> <RevertButton bind:value {defaultValue} />
</Col> </Col>

View file

@ -3,8 +3,8 @@
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
--> -->
<script lang="ts"> <script lang="ts">
import Row from "./Row.svelte"; import Row from "../components/Row.svelte";
import Col from "./Col.svelte"; import Col from "../components/Col.svelte";
import Label from "./Label.svelte"; import Label from "./Label.svelte";
import TooltipLabel from "./TooltipLabel.svelte"; import TooltipLabel from "./TooltipLabel.svelte";
import Switch from "./Switch.svelte"; import Switch from "./Switch.svelte";
@ -17,13 +17,13 @@
const id = Math.random().toString(36).substring(2); const id = Math.random().toString(36).substring(2);
</script> </script>
<Row> <Row --cols={6}>
<Col> <Col --col-size={4}
{#if markdownTooltip}<TooltipLabel for={id} {markdownTooltip} >{#if markdownTooltip}<TooltipLabel for={id} {markdownTooltip}
><slot /></TooltipLabel ><slot /></TooltipLabel
>{:else}<Label for={id}><slot /></Label>{/if} >{:else}<Label for={id}><slot /></Label>{/if}</Col
</Col> >
<Col grow={false}> <Col --col-justify="flex-end">
<Switch {id} bind:value /> <Switch {id} bind:value />
<RevertButton bind:value {defaultValue} /> <RevertButton bind:value {defaultValue} />
</Col> </Col>

View file

@ -3,26 +3,20 @@ Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
--> -->
<script lang="ts"> <script lang="ts">
import Section from "../components/Section.svelte"; import Container from "../components/Container.svelte";
export let title: string; export let title: string;
export let api: Record<string, never> | undefined = undefined; export let api: Record<string, never> | undefined = undefined;
</script> </script>
<div class="container-fluid mb-5"> <Container --gutter-block="2px" --container-margin="0" {api}>
<h1>{title}</h1> <h1>{title}</h1>
<Section {api}> <slot />
<slot /> </Container>
</Section>
</div>
<style lang="scss"> <style lang="scss">
h1 { h1 {
border-bottom: 1px solid var(--medium-border); border-bottom: 1px solid var(--medium-border);
} }
.container-fluid {
break-inside: avoid;
}
</style> </style>

View file

@ -29,6 +29,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
> >
<Badge <Badge
class="opacity-50" class="opacity-50"
iconSize={85}
on:mount={(event) => createTooltip(event.detail.span)} on:mount={(event) => createTooltip(event.detail.span)}
> >
{@html infoCircle} {@html infoCircle}

View file

@ -4,7 +4,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
--> -->
<script lang="ts"> <script lang="ts">
import { slide } from "svelte/transition"; import { slide } from "svelte/transition";
import Row from "./Row.svelte"; import Row from "../components/Row.svelte";
import { withoutUnicodeIsolation } from "../lib/i18n/"; import { withoutUnicodeIsolation } from "../lib/i18n/";
export let warning: string; export let warning: string;

View file

@ -1,7 +1,5 @@
@import "sass/base"; @import "sass/base";
@import "sass/bootstrap/scss/containers";
@import "sass/bootstrap/scss/grid";
@import "sass/bootstrap/scss/dropdown"; @import "sass/bootstrap/scss/dropdown";
@import "sass/bootstrap/scss/buttons"; @import "sass/bootstrap/scss/buttons";
@import "sass/bootstrap/scss/button-group"; @import "sass/bootstrap/scss/button-group";

View file

@ -37,7 +37,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
</script> </script>
<script lang="ts"> <script lang="ts">
import StickyHeader from "../components/StickyHeader.svelte"; import StickyContainer from "../components/StickyContainer.svelte";
import ButtonToolbar from "../components/ButtonToolbar.svelte"; import ButtonToolbar from "../components/ButtonToolbar.svelte";
import Item from "../components/Item.svelte"; import Item from "../components/Item.svelte";
@ -72,7 +72,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
} as EditorToolbarAPI); } as EditorToolbarAPI);
</script> </script>
<StickyHeader> <StickyContainer --gutter-block="0.1rem" --sticky-borders="0 0 1px">
<ButtonToolbar {size} {wrap} api={toolbar}> <ButtonToolbar {size} {wrap} api={toolbar}>
<Item id="notetype"> <Item id="notetype">
<NoteTypeButtons api={notetypeButtons} /> <NoteTypeButtons api={notetypeButtons} />
@ -94,4 +94,4 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
<TemplateButtons api={templateButtons} /> <TemplateButtons api={templateButtons} />
</Item> </Item>
</ButtonToolbar> </ButtonToolbar>
</StickyHeader> </StickyContainer>

View file

@ -5,7 +5,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
<script lang="ts"> <script lang="ts">
import { createEventDispatcher, tick } from "svelte"; import { createEventDispatcher, tick } from "svelte";
import type { Writable } from "svelte/store"; import type { Writable } from "svelte/store";
import StickyFooter from "../components/StickyFooter.svelte"; import StickyContainer from "../components/StickyContainer.svelte";
import TagOptionsBadge from "./TagOptionsBadge.svelte"; import TagOptionsBadge from "./TagOptionsBadge.svelte";
import TagEditMode from "./TagEditMode.svelte"; import TagEditMode from "./TagEditMode.svelte";
import TagInput from "./TagInput.svelte"; import TagInput from "./TagInput.svelte";
@ -395,7 +395,12 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
$: shortenTags = shortenTags || assumedRows > 2; $: shortenTags = shortenTags || assumedRows > 2;
</script> </script>
<StickyFooter bind:height class="d-flex"> <StickyContainer
--gutter-block="0.1rem"
--sticky-borders="1px 0 0"
bind:height
class="d-flex"
>
{#if !wrap} {#if !wrap}
<TagOptionsBadge <TagOptionsBadge
showSelectionsOptions={tagTypes.some((tag) => tag.selected)} showSelectionsOptions={tagTypes.some((tag) => tag.selected)}
@ -504,7 +509,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
<Tag>SPACER</Tag> <Tag>SPACER</Tag>
</div> </div>
</ButtonToolbar> </ButtonToolbar>
</StickyFooter> </StickyContainer>
<style lang="scss"> <style lang="scss">
.tag-spacer { .tag-spacer {

View file

@ -12,7 +12,7 @@
{ "path": "reviewer" }, { "path": "reviewer" },
{ "path": "lib" }, { "path": "lib" },
{ "path": "domlib" }, { "path": "domlib" },
{ "path": "sveltelib" }, { "path": "sveltelib" }
], ],
"compilerOptions": { "compilerOptions": {
"declaration": true, "declaration": true,