update ts deps

- prettier's formatting has changed, so files needed to be reformatted
- dart is spitting out deprecation warnings like:

254 │   2: $spacer / 2,
    │      ^^^^^^^^^^^
    ╵
    bazel-out/darwin-fastbuild/bin/ts/sass/bootstrap/_variables.scss 254:6  @import
    ts/sass/button_mixins.scss 2:9                                          @use
    ts/components/ColorPicker.svelte 2:5                                    root stylesheet

DEPRECATION WARNING: Using / for division is deprecated and will be removed in Dart Sass 2.0.0.

Recommendation: math.div($grid-gutter-width, 2)
This commit is contained in:
Damien Elmes 2021-05-26 09:21:33 +10:00
parent cdc40c1ce4
commit 0026506543
69 changed files with 1429 additions and 1531 deletions

View File

@ -61,14 +61,17 @@ async function _updateQA(
onShownHook = [onshown]; onShownHook = [onshown];
const qa = document.getElementById("qa")!; const qa = document.getElementById("qa")!;
const renderError = (kind: string) => (error: Error): void => { const renderError =
const errorMessage = String(error).substring(0, 2000); (kind: string) =>
const errorStack = String(error.stack).substring(0, 2000); (error: Error): void => {
qa.innerHTML = `Invalid ${kind} on card: ${errorMessage}\n${errorStack}`.replace( const errorMessage = String(error).substring(0, 2000);
/\n/g, const errorStack = String(error.stack).substring(0, 2000);
"<br>" qa.innerHTML =
); `Invalid ${kind} on card: ${errorMessage}\n${errorStack}`.replace(
}; /\n/g,
"<br>"
);
};
// hide current card // hide current card
qa.style.opacity = "0"; qa.style.opacity = "0";

View File

@ -18,6 +18,17 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
setContext(dropdownKey, null); setContext(dropdownKey, null);
</script> </script>
<WithTheming style="--toolbar-wrap: nowrap; ">
<ButtonToolbar
{id}
class={`dropdown-menu btn-dropdown-menu ${className}`}
nowrap={true}
{api}
>
<slot />
</ButtonToolbar>
</WithTheming>
<style lang="scss"> <style lang="scss">
:global(.dropdown-menu.btn-dropdown-menu) { :global(.dropdown-menu.btn-dropdown-menu) {
display: none; display: none;
@ -36,13 +47,3 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
display: flex; display: flex;
} }
</style> </style>
<WithTheming style="--toolbar-wrap: nowrap; ">
<ButtonToolbar
{id}
class={`dropdown-menu btn-dropdown-menu ${className}`}
nowrap={true}
{api}>
<slot />
</ButtonToolbar>
</WithTheming>

View File

@ -24,12 +24,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
return { detach, position }; return { detach, position };
} }
const { const { registerComponent, items, dynamicItems, getDynamicInterface } =
registerComponent, makeInterface(makeRegistration);
items,
dynamicItems,
getDynamicInterface,
} = makeInterface(makeRegistration);
$: for (const [index, item] of $items.entries()) { $: for (const [index, item] of $items.entries()) {
if ($items.length === 1) { if ($items.length === 1) {
@ -49,9 +45,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
let buttonGroupRef: HTMLDivElement; let buttonGroupRef: HTMLDivElement;
$: if (api && buttonGroupRef) { $: if (api && buttonGroupRef) {
const { addComponent, updateRegistration } = getDynamicInterface( const { addComponent, updateRegistration } =
buttonGroupRef getDynamicInterface(buttonGroupRef);
);
const insertButton = (button: SvelteComponent, position: Identifier = 0) => const insertButton = (button: SvelteComponent, position: Identifier = 0) =>
addComponent(button, (added, parent) => insert(added, parent, position)); addComponent(button, (added, parent) => insert(added, parent, position));
@ -78,20 +73,13 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
} }
</script> </script>
<style lang="scss">
div {
flex-wrap: var(--toolbar-wrap);
padding: calc(var(--toolbar-size) / 10);
margin: 0;
}
</style>
<div <div
bind:this={buttonGroupRef} bind:this={buttonGroupRef}
{id} {id}
class={`btn-group ${className}`} class={`btn-group ${className}`}
dir="ltr" dir="ltr"
role="group"> role="group"
>
<slot /> <slot />
{#each $dynamicItems as item} {#each $dynamicItems as item}
<ButtonGroupItem id={item[0].id} registration={item[1]}> <ButtonGroupItem id={item[0].id} registration={item[1]}>
@ -99,3 +87,11 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
</ButtonGroupItem> </ButtonGroupItem>
{/each} {/each}
</div> </div>
<style lang="scss">
div {
flex-wrap: var(--toolbar-wrap);
padding: calc(var(--toolbar-size) / 10);
margin: 0;
}
</style>

View File

@ -44,9 +44,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
detach.subscribe((value: boolean) => (detached = value)); detach.subscribe((value: boolean) => (detached = value));
position.subscribe((value: ButtonPosition) => (position_ = value)); position.subscribe((value: ButtonPosition) => (position_ = value));
} else if (hasContext(buttonGroupKey)) { } else if (hasContext(buttonGroupKey)) {
const registerComponent = getContext<Register<ButtonRegistration>>( const registerComponent =
buttonGroupKey getContext<Register<ButtonRegistration>>(buttonGroupKey);
);
const { detach, position } = registerComponent(); const { detach, position } = registerComponent();
detach.subscribe((value: boolean) => (detached = value)); detach.subscribe((value: boolean) => (detached = value));
position.subscribe((value: ButtonPosition) => (position_ = value)); position.subscribe((value: ButtonPosition) => (position_ = value));

View File

@ -24,9 +24,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
return { detach }; return { detach };
} }
const { registerComponent, dynamicItems, getDynamicInterface } = makeInterface( const { registerComponent, dynamicItems, getDynamicInterface } =
makeRegistration makeInterface(makeRegistration);
);
setContext(buttonToolbarKey, registerComponent); setContext(buttonToolbarKey, registerComponent);
@ -34,9 +33,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
let buttonToolbarRef: HTMLDivElement; let buttonToolbarRef: HTMLDivElement;
$: if (buttonToolbarRef && api) { $: if (buttonToolbarRef && api) {
const { addComponent, updateRegistration } = getDynamicInterface( const { addComponent, updateRegistration } =
buttonToolbarRef getDynamicInterface(buttonToolbarRef);
);
const insertGroup = (group: SvelteComponent, position: Identifier = 0) => const insertGroup = (group: SvelteComponent, position: Identifier = 0) =>
addComponent(group, (added, parent) => insert(added, parent, position)); addComponent(group, (added, parent) => insert(added, parent, position));
@ -68,7 +66,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
{id} {id}
class={`btn-toolbar ${className}`} class={`btn-toolbar ${className}`}
class:flex-nowrap={nowrap} class:flex-nowrap={nowrap}
role="toolbar"> role="toolbar"
>
<slot /> <slot />
{#each $dynamicItems as item} {#each $dynamicItems as item}
<ButtonToolbarItem id={item[0].id} registration={item[1]}> <ButtonToolbarItem id={item[0].id} registration={item[1]}>

View File

@ -20,9 +20,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
const { detach } = registration; const { detach } = registration;
detach.subscribe((value: boolean) => (detached = value)); detach.subscribe((value: boolean) => (detached = value));
} else if (hasContext(buttonToolbarKey)) { } else if (hasContext(buttonToolbarKey)) {
const registerComponent = getContext<Register<ButtonGroupRegistration>>( const registerComponent =
buttonToolbarKey getContext<Register<ButtonGroupRegistration>>(buttonToolbarKey);
);
const { detach } = registerComponent(); const { detach } = registerComponent();
detach.subscribe((value: boolean) => (detached = value)); detach.subscribe((value: boolean) => (detached = value));
} else { } else {
@ -30,15 +29,15 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
} }
</script> </script>
<style lang="scss">
div {
display: contents;
}
</style>
<!-- div is necessary to preserve item position --> <!-- div is necessary to preserve item position -->
<div {id}> <div {id}>
<Detachable {detached}> <Detachable {detached}>
<slot /> <slot />
</Detachable> </Detachable>
</div> </div>
<style lang="scss">
div {
display: contents;
}
</style>

View File

@ -25,6 +25,20 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
onMount(() => dispatch("mount", { button: buttonRef, input: inputRef })); onMount(() => dispatch("mount", { button: buttonRef, input: inputRef }));
</script> </script>
<button
bind:this={buttonRef}
tabindex="-1"
{id}
class={`btn ${className}`}
class:btn-day={!nightMode}
class:btn-night={nightMode}
title={tooltip}
on:click={delegateToInput}
on:mousedown|preventDefault
>
<input tabindex="-1" bind:this={inputRef} type="color" on:change />
</button>
<style lang="scss"> <style lang="scss">
@use "ts/sass/button_mixins" as button; @use "ts/sass/button_mixins" as button;
@ -59,16 +73,3 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
opacity: 0; opacity: 0;
} }
</style> </style>
<button
bind:this={buttonRef}
tabindex="-1"
{id}
class={`btn ${className}`}
class:btn-day={!nightMode}
class:btn-night={nightMode}
title={tooltip}
on:click={delegateToInput}
on:mousedown|preventDefault>
<input tabindex="-1" bind:this={inputRef} type="color" on:change />
</button>

View File

@ -20,6 +20,19 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
onMount(() => dispatch("mount", { button: buttonRef })); onMount(() => dispatch("mount", { button: buttonRef }));
</script> </script>
<button
{id}
bind:this={buttonRef}
class={`btn dropdown-item ${className}`}
class:btn-day={!nightMode}
class:btn-night={nightMode}
title={tooltip}
on:click
on:mousedown|preventDefault
>
<slot />
</button>
<style lang="scss"> <style lang="scss">
@use 'ts/sass/button_mixins' as button; @use 'ts/sass/button_mixins' as button;
@ -57,15 +70,3 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
} }
} }
</style> </style>
<button
{id}
bind:this={buttonRef}
class={`btn dropdown-item ${className}`}
class:btn-day={!nightMode}
class:btn-night={nightMode}
title={tooltip}
on:click
on:mousedown|preventDefault>
<slot />
</button>

View File

@ -11,13 +11,13 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
setContext(dropdownKey, null); setContext(dropdownKey, null);
</script> </script>
<div {id} class="dropdown-menu">
<slot />
</div>
<style lang="scss"> <style lang="scss">
div { div {
background-color: var(--frame-bg); background-color: var(--frame-bg);
border-color: var(--medium-border); border-color: var(--medium-border);
} }
</style> </style>
<div {id} class="dropdown-menu">
<slot />
</div>

View File

@ -29,6 +29,24 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
onMount(() => dispatch("mount", { button: buttonRef })); onMount(() => dispatch("mount", { button: buttonRef }));
</script> </script>
<button
bind:this={buttonRef}
{id}
class={`btn ${className}`}
class:active
class:dropdown-toggle={dropdownProps.dropdown}
class:btn-day={!nightMode}
class:btn-night={nightMode}
title={tooltip}
{...dropdownProps}
disabled={_disabled}
tabindex={tabbable ? 0 : -1}
on:click
on:mousedown|preventDefault
>
<span class="p-1"><slot /></span>
</button>
<style lang="scss"> <style lang="scss">
@use "ts/sass/button_mixins" as button; @use "ts/sass/button_mixins" as button;
@ -61,20 +79,3 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
margin-right: 0.25rem; margin-right: 0.25rem;
} }
</style> </style>
<button
bind:this={buttonRef}
{id}
class={`btn ${className}`}
class:active
class:dropdown-toggle={dropdownProps.dropdown}
class:btn-day={!nightMode}
class:btn-night={nightMode}
title={tooltip}
{...dropdownProps}
disabled={_disabled}
tabindex={tabbable ? 0 : -1}
on:click
on:mousedown|preventDefault>
<span class="p-1"><slot /></span>
</button>

View File

@ -34,6 +34,24 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
onMount(() => dispatch("mount", { button: buttonRef })); onMount(() => dispatch("mount", { button: buttonRef }));
</script> </script>
<button
bind:this={buttonRef}
{id}
class={extendClassName(className, theme)}
class:active
class:dropdown-toggle={dropdownProps.dropdown}
class:btn-day={theme === "anki" && !nightMode}
class:btn-night={theme === "anki" && nightMode}
title={tooltip}
{...dropdownProps}
disabled={_disabled}
tabindex={tabbable ? 0 : -1}
on:click
on:mousedown|preventDefault
>
<slot />
</button>
<style lang="scss"> <style lang="scss">
@use "ts/sass/button_mixins" as button; @use "ts/sass/button_mixins" as button;
@ -48,20 +66,3 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
@include button.btn-day; @include button.btn-day;
@include button.btn-night; @include button.btn-night;
</style> </style>
<button
bind:this={buttonRef}
{id}
class={extendClassName(className, theme)}
class:active
class:dropdown-toggle={dropdownProps.dropdown}
class:btn-day={theme === 'anki' && !nightMode}
class:btn-night={theme === 'anki' && nightMode}
title={tooltip}
{...dropdownProps}
disabled={_disabled}
tabindex={tabbable ? 0 : -1}
on:click
on:mousedown|preventDefault>
<slot />
</button>

View File

@ -25,17 +25,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
onMount(() => dispatch("mount", { button: buttonRef })); onMount(() => dispatch("mount", { button: buttonRef }));
</script> </script>
<style lang="scss">
@use "ts/sass/button_mixins" as button;
select {
height: var(--toolbar-size);
}
@include button.btn-day($with-hover: false);
@include button.btn-night($with-hover: false);
</style>
<!-- svelte-ignore a11y-no-onchange --> <!-- svelte-ignore a11y-no-onchange -->
<select <select
@ -47,6 +36,18 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
class:btn-day={!nightMode} class:btn-day={!nightMode}
class:btn-night={nightMode} class:btn-night={nightMode}
title={tooltip} title={tooltip}
on:change> on:change
>
<slot /> <slot />
</select> </select>
<style lang="scss">
@use "ts/sass/button_mixins" as button;
select {
height: var(--toolbar-size);
}
@include button.btn-day($with-hover: false);
@include button.btn-night($with-hover: false);
</style>

View File

@ -8,6 +8,10 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
export { className as class }; export { className as class };
</script> </script>
<nav {id} class={`pb-1 pt-1 ${className}`}>
<slot />
</nav>
<style lang="scss"> <style lang="scss">
nav { nav {
position: sticky; position: sticky;
@ -20,7 +24,3 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
border-bottom: 1px solid var(--medium-border); border-bottom: 1px solid var(--medium-border);
} }
</style> </style>
<nav {id} class={`pb-1 pt-1 ${className}`}>
<slot />
</nav>

View File

@ -12,16 +12,16 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
export let button: ToolbarItem; export let button: ToolbarItem;
</script> </script>
<style lang="scss">
label {
display: flex;
padding: 0 calc(var(--toolbar-size) / 10);
}
</style>
<!-- svelte-ignore a11y-label-has-associated-control --> <!-- svelte-ignore a11y-label-has-associated-control -->
<label {id} class={className}> <label {id} class={className}>
<span class="me-1">{label}</span> <span class="me-1">{label}</span>
<svelte:component this={button.component} {...button} /> <svelte:component this={button.component} {...button} />
</label> </label>
<style lang="scss">
label {
display: flex;
padding: 0 calc(var(--toolbar-size) / 10);
}
</style>

View File

@ -13,17 +13,15 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
const stateStore = writable(stateMap); const stateStore = writable(stateMap);
function updateAllStateWithCallback(callback: (key: string) => boolean): void { function updateAllStateWithCallback(callback: (key: string) => boolean): void {
stateStore.update( stateStore.update((map: StateMap): StateMap => {
(map: StateMap): StateMap => { const newMap = new Map() as StateMap;
const newMap = new Map() as StateMap;
for (const key of map.keys()) { for (const key of map.keys()) {
newMap.set(key, callback(key)); newMap.set(key, callback(key));
}
return newMap;
} }
);
return newMap;
});
} }
export function updateAllState(event: Event): void { export function updateAllState(event: Event): void {
@ -37,12 +35,10 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
} }
function updateStateByKey(key: string, event: Event): void { function updateStateByKey(key: string, event: Event): void {
stateStore.update( stateStore.update((map: StateMap): StateMap => {
(map: StateMap): StateMap => { map.set(key, updaterMap.get(key)!(event));
map.set(key, updaterMap.get(key)!(event)); return map;
return map; });
}
);
} }
</script> </script>

View File

@ -9,12 +9,12 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
export let style: string; export let style: string;
</script> </script>
<div {id} class={className} {style}>
<slot />
</div>
<style lang="scss"> <style lang="scss">
div { div {
display: contents; display: contents;
} }
</style> </style>
<div {id} class={className} {style}>
<slot />
</div>

View File

@ -23,22 +23,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
}); });
</script> </script>
<style>
.congrats-outer {
display: flex;
justify-content: center;
}
.congrats-inner {
max-width: 30em;
}
.description {
border: 1px solid var(--border);
padding: 1em;
}
</style>
<div class="congrats-outer"> <div class="congrats-outer">
<div class="congrats-inner"> <div class="congrats-inner">
<h3>{congrats}</h3> <h3>{congrats}</h3>
@ -74,3 +58,19 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
{/if} {/if}
</div> </div>
</div> </div>
<style>
.congrats-outer {
display: flex;
justify-content: center;
}
.congrats-inner {
max-width: 30em;
}
.description {
border: 1px solid var(--border);
padding: 1em;
}
</style>

View File

@ -21,7 +21,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
min={1} min={1}
max={365 * 100} max={365 * 100}
defaultValue={defaults.maximumReviewInterval} defaultValue={defaults.maximumReviewInterval}
bind:value={$config.maximumReviewInterval} /> bind:value={$config.maximumReviewInterval}
/>
<SpinBoxFloat <SpinBoxFloat
label={tr.schedulingStartingEase()} label={tr.schedulingStartingEase()}
@ -30,7 +31,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
max={5} max={5}
defaultValue={defaults.initialEase} defaultValue={defaults.initialEase}
value={$config.initialEase} value={$config.initialEase}
on:changed={(evt) => ($config.initialEase = evt.detail.value)} /> on:changed={(evt) => ($config.initialEase = evt.detail.value)}
/>
<SpinBoxFloat <SpinBoxFloat
label={tr.schedulingEasyBonus()} label={tr.schedulingEasyBonus()}
@ -39,7 +41,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
max={3} max={3}
defaultValue={defaults.easyMultiplier} defaultValue={defaults.easyMultiplier}
value={$config.easyMultiplier} value={$config.easyMultiplier}
on:changed={(evt) => ($config.easyMultiplier = evt.detail.value)} /> on:changed={(evt) => ($config.easyMultiplier = evt.detail.value)}
/>
<SpinBoxFloat <SpinBoxFloat
label={tr.schedulingIntervalModifier()} label={tr.schedulingIntervalModifier()}
@ -48,7 +51,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
max={2} max={2}
defaultValue={defaults.intervalMultiplier} defaultValue={defaults.intervalMultiplier}
value={$config.intervalMultiplier} value={$config.intervalMultiplier}
on:changed={(evt) => ($config.intervalMultiplier = evt.detail.value)} /> on:changed={(evt) => ($config.intervalMultiplier = evt.detail.value)}
/>
<SpinBoxFloat <SpinBoxFloat
label={tr.schedulingHardInterval()} label={tr.schedulingHardInterval()}
@ -57,7 +61,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
max={1.3} max={1.3}
defaultValue={defaults.hardMultiplier} defaultValue={defaults.hardMultiplier}
value={$config.hardMultiplier} value={$config.hardMultiplier}
on:changed={(evt) => ($config.hardMultiplier = evt.detail.value)} /> on:changed={(evt) => ($config.hardMultiplier = evt.detail.value)}
/>
<SpinBoxFloat <SpinBoxFloat
label={tr.schedulingNewInterval()} label={tr.schedulingNewInterval()}
@ -66,4 +71,5 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
max={1} max={1}
defaultValue={defaults.lapseMultiplier} defaultValue={defaults.lapseMultiplier}
value={$config.lapseMultiplier} value={$config.lapseMultiplier}
on:changed={(evt) => ($config.lapseMultiplier = evt.detail.value)} /> on:changed={(evt) => ($config.lapseMultiplier = evt.detail.value)}
/>

View File

@ -18,10 +18,12 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
label={tr.deckConfigBuryNewSiblings()} label={tr.deckConfigBuryNewSiblings()}
tooltip={tr.deckConfigBuryTooltip()} tooltip={tr.deckConfigBuryTooltip()}
defaultValue={defaults.buryNew} defaultValue={defaults.buryNew}
bind:value={$config.buryNew} /> bind:value={$config.buryNew}
/>
<CheckBox <CheckBox
label={tr.deckConfigBuryReviewSiblings()} label={tr.deckConfigBuryReviewSiblings()}
tooltip={tr.deckConfigBuryTooltip()} tooltip={tr.deckConfigBuryTooltip()}
defaultValue={defaults.buryReviews} defaultValue={defaults.buryReviews}
bind:value={$config.buryReviews} /> bind:value={$config.buryReviews}
/>

View File

@ -12,12 +12,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
export let id: string | undefined = undefined; export let id: string | undefined = undefined;
</script> </script>
<style lang="scss">
.checkbox-outer {
margin-top: 0.5em;
}
</style>
<ConfigEntry {id} label="" wholeLine={true} bind:value {defaultValue}> <ConfigEntry {id} label="" wholeLine={true} bind:value {defaultValue}>
<div class="checkbox-outer"> <div class="checkbox-outer">
<label> <input type="checkbox" bind:checked={value} /> {label} </label> <label> <input type="checkbox" bind:checked={value} /> {label} </label>
@ -26,3 +20,9 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
{/if} {/if}
</div> </div>
</ConfigEntry> </ConfigEntry>
<style lang="scss">
.checkbox-outer {
margin-top: 0.5em;
}
</style>

View File

@ -16,6 +16,19 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
export let state: DeckOptionsState; export let state: DeckOptionsState;
</script> </script>
<div class="outer">
<DailyLimits {state} />
<NewOptions {state} />
<LapseOptions {state} />
<BuryOptions {state} />
{#if state.v3Scheduler}
<DisplayOrder {state} />
{/if}
<GeneralOptions {state} />
<Addons {state} />
<AdvancedOptions {state} />
</div>
<style lang="scss"> <style lang="scss">
:global(h2) { :global(h2) {
margin-top: 1em; margin-top: 1em;
@ -33,16 +46,3 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
padding-left: 16px; padding-left: 16px;
} }
</style> </style>
<div class="outer">
<DailyLimits {state} />
<NewOptions {state} />
<LapseOptions {state} />
<BuryOptions {state} />
{#if state.v3Scheduler}
<DisplayOrder {state} />
{/if}
<GeneralOptions {state} />
<Addons {state} />
<AdvancedOptions {state} />
</div>

View File

@ -21,6 +21,32 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
$: renderedTooltip = marked(tooltip); $: renderedTooltip = marked(tooltip);
</script> </script>
<div {id} class="outer">
{#if label}
<div class="table">
<span class="vcenter">
{label}
{#if renderedTooltip}
<HelpPopup html={renderedTooltip} />
{/if}
</span>
</div>
{/if}
<div class="input-grid" class:full-grid-width={wholeLine}>
<slot />
<RevertButton bind:value {defaultValue} on:revert />
</div>
<div class="full-grid-width">
{#each warnings as warning}
{#if warning}
<div class="alert alert-warning" in:slide out:slide>{warning}</div>
{/if}
{/each}
</div>
</div>
<style lang="scss"> <style lang="scss">
.outer { .outer {
display: grid; display: grid;
@ -52,29 +78,3 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
grid-template-columns: 10fr 16px; grid-template-columns: 10fr 16px;
} }
</style> </style>
<div {id} class="outer">
{#if label}
<div class="table">
<span class="vcenter">
{label}
{#if renderedTooltip}
<HelpPopup html={renderedTooltip} />
{/if}
</span>
</div>
{/if}
<div class="input-grid" class:full-grid-width={wholeLine}>
<slot />
<RevertButton bind:value {defaultValue} on:revert />
</div>
<div class="full-grid-width">
{#each warnings as warning}
{#if warning}
<div class="alert alert-warning" in:slide out:slide>{warning}</div>
{/if}
{/each}
</div>
</div>

View File

@ -64,13 +64,15 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
title="Add Config" title="Add Config"
prompt="Name" prompt="Name"
onOk={onAddConfig} onOk={onAddConfig}
bind:modalKey={addModalKey} /> bind:modalKey={addModalKey}
/>
<TextInputModal <TextInputModal
title="Rename Config" title="Rename Config"
prompt="Name" prompt="Name"
onOk={onRenameConfig} onOk={onRenameConfig}
value={oldName} value={oldName}
bind:modalKey={renameModalKey} /> bind:modalKey={renameModalKey}
/>
<StickyBar> <StickyBar>
<WithTheming style="--toolbar-size: 2.3rem; --toolbar-wrap: nowrap"> <WithTheming style="--toolbar-size: 2.3rem; --toolbar-wrap: nowrap">
@ -82,7 +84,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
{#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}

View File

@ -41,7 +41,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
min={0} min={0}
warnings={[newCardsGreaterThanParent]} warnings={[newCardsGreaterThanParent]}
defaultValue={defaults.newPerDay} defaultValue={defaults.newPerDay}
bind:value={$config.newPerDay} /> bind:value={$config.newPerDay}
/>
<SpinBox <SpinBox
label={tr.schedulingMaximumReviewsday()} label={tr.schedulingMaximumReviewsday()}
@ -49,4 +50,5 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
min={0} min={0}
warnings={[reviewsTooLow]} warnings={[reviewsTooLow]}
defaultValue={defaults.reviewsPerDay} defaultValue={defaults.reviewsPerDay}
bind:value={$config.reviewsPerDay} /> bind:value={$config.reviewsPerDay}
/>

View File

@ -37,32 +37,37 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
tooltip={tr.deckConfigNewGatherPriorityTooltip()} tooltip={tr.deckConfigNewGatherPriorityTooltip()}
choices={newGatherPriorityChoices} choices={newGatherPriorityChoices}
defaultValue={defaults.newCardGatherPriority} defaultValue={defaults.newCardGatherPriority}
bind:value={$config.newCardGatherPriority} /> bind:value={$config.newCardGatherPriority}
/>
<EnumSelector <EnumSelector
label={tr.deckConfigNewCardSortOrder()} label={tr.deckConfigNewCardSortOrder()}
tooltip={tr.deckConfigNewCardSortOrderTooltip()} tooltip={tr.deckConfigNewCardSortOrderTooltip()}
choices={newSortOrderChoices} choices={newSortOrderChoices}
defaultValue={defaults.newCardSortOrder} defaultValue={defaults.newCardSortOrder}
bind:value={$config.newCardSortOrder} /> bind:value={$config.newCardSortOrder}
/>
<EnumSelector <EnumSelector
label={tr.deckConfigNewReviewPriority()} label={tr.deckConfigNewReviewPriority()}
tooltip={tr.deckConfigNewReviewPriorityTooltip()} tooltip={tr.deckConfigNewReviewPriorityTooltip()}
choices={reviewMixChoices()} choices={reviewMixChoices()}
defaultValue={defaults.newMix} defaultValue={defaults.newMix}
bind:value={$config.newMix} /> bind:value={$config.newMix}
/>
<EnumSelector <EnumSelector
label={tr.deckConfigInterdayStepPriority()} label={tr.deckConfigInterdayStepPriority()}
tooltip={tr.deckConfigInterdayStepPriorityTooltip()} tooltip={tr.deckConfigInterdayStepPriorityTooltip()}
choices={reviewMixChoices()} choices={reviewMixChoices()}
defaultValue={defaults.interdayLearningMix} defaultValue={defaults.interdayLearningMix}
bind:value={$config.interdayLearningMix} /> bind:value={$config.interdayLearningMix}
/>
<EnumSelector <EnumSelector
label={tr.deckConfigReviewSortOrder()} label={tr.deckConfigReviewSortOrder()}
tooltip={tr.deckConfigReviewSortOrderTooltip()} tooltip={tr.deckConfigReviewSortOrderTooltip()}
choices={reviewOrderChoices} choices={reviewOrderChoices}
defaultValue={defaults.reviewOrder} defaultValue={defaults.reviewOrder}
bind:value={$config.reviewOrder} /> bind:value={$config.reviewOrder}
/>

View File

@ -21,24 +21,28 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
min={30} min={30}
max={600} max={600}
defaultValue={defaults.capAnswerTimeToSecs} defaultValue={defaults.capAnswerTimeToSecs}
bind:value={$config.capAnswerTimeToSecs} /> bind:value={$config.capAnswerTimeToSecs}
/>
<CheckBox <CheckBox
id="showAnswerTimer" id="showAnswerTimer"
label={tr.schedulingShowAnswerTimer()} label={tr.schedulingShowAnswerTimer()}
tooltip={tr.deckConfigShowAnswerTimerTooltip()} tooltip={tr.deckConfigShowAnswerTimerTooltip()}
defaultValue={defaults.showTimer} defaultValue={defaults.showTimer}
bind:value={$config.showTimer} /> bind:value={$config.showTimer}
/>
<h2>{tr.deckConfigAudioTitle()}</h2> <h2>{tr.deckConfigAudioTitle()}</h2>
<CheckBox <CheckBox
label={tr.deckConfigDisableAutoplay()} label={tr.deckConfigDisableAutoplay()}
defaultValue={defaults.disableAutoplay} defaultValue={defaults.disableAutoplay}
bind:value={$config.disableAutoplay} /> bind:value={$config.disableAutoplay}
/>
<CheckBox <CheckBox
label={tr.schedulingAlwaysIncludeQuestionSideWhenReplaying()} label={tr.schedulingAlwaysIncludeQuestionSideWhenReplaying()}
tooltip={tr.deckConfigAlwaysIncludeQuestionAudioTooltip()} tooltip={tr.deckConfigAlwaysIncludeQuestionAudioTooltip()}
defaultValue={defaults.skipQuestionWhenReplayingAnswer} defaultValue={defaults.skipQuestionWhenReplayingAnswer}
bind:value={$config.skipQuestionWhenReplayingAnswer} /> bind:value={$config.skipQuestionWhenReplayingAnswer}
/>

View File

@ -20,13 +20,13 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
}); });
</script> </script>
<span bind:this={ref} title={html}>
{@html infoCircle}
</span>
<style> <style>
span :global(svg) { span :global(svg) {
vertical-align: text-bottom; vertical-align: text-bottom;
opacity: 0.3; opacity: 0.3;
} }
</style> </style>
<span bind:this={ref} title={html}>
{@html infoCircle}
</span>

View File

@ -35,7 +35,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
tooltip={tr.deckConfigRelearningStepsTooltip()} tooltip={tr.deckConfigRelearningStepsTooltip()}
defaultValue={defaults.relearnSteps} defaultValue={defaults.relearnSteps}
value={$config.relearnSteps} value={$config.relearnSteps}
on:changed={(evt) => ($config.relearnSteps = evt.detail.value)} /> on:changed={(evt) => ($config.relearnSteps = evt.detail.value)}
/>
<SpinBox <SpinBox
label={tr.schedulingMinimumInterval()} label={tr.schedulingMinimumInterval()}
@ -43,19 +44,22 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
warnings={[stepsExceedMinimumInterval]} warnings={[stepsExceedMinimumInterval]}
min={1} min={1}
defaultValue={defaults.minimumLapseInterval} defaultValue={defaults.minimumLapseInterval}
bind:value={$config.minimumLapseInterval} /> bind:value={$config.minimumLapseInterval}
/>
<SpinBox <SpinBox
label={tr.schedulingLeechThreshold()} label={tr.schedulingLeechThreshold()}
tooltip={tr.deckConfigLeechThresholdTooltip()} tooltip={tr.deckConfigLeechThresholdTooltip()}
min={1} min={1}
defaultValue={defaults.leechThreshold} defaultValue={defaults.leechThreshold}
bind:value={$config.leechThreshold} /> bind:value={$config.leechThreshold}
/>
<EnumSelector <EnumSelector
label={tr.schedulingLeechAction()} label={tr.schedulingLeechAction()}
tooltip={tr.deckConfigLeechActionTooltip()} tooltip={tr.deckConfigLeechActionTooltip()}
choices={leechChoices} choices={leechChoices}
defaultValue={defaults.leechAction} defaultValue={defaults.leechAction}
bind:value={$config.leechAction} /> bind:value={$config.leechAction}
/>
</div> </div>

View File

@ -42,25 +42,29 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
tooltip={tr.deckConfigLearningStepsTooltip()} tooltip={tr.deckConfigLearningStepsTooltip()}
defaultValue={defaults.learnSteps} defaultValue={defaults.learnSteps}
value={$config.learnSteps} value={$config.learnSteps}
on:changed={(evt) => ($config.learnSteps = evt.detail.value)} /> on:changed={(evt) => ($config.learnSteps = evt.detail.value)}
/>
<SpinBox <SpinBox
label={tr.schedulingGraduatingInterval()} label={tr.schedulingGraduatingInterval()}
tooltip={tr.deckConfigGraduatingIntervalTooltip()} tooltip={tr.deckConfigGraduatingIntervalTooltip()}
warnings={[stepsExceedGraduatingInterval]} warnings={[stepsExceedGraduatingInterval]}
defaultValue={defaults.graduatingIntervalGood} defaultValue={defaults.graduatingIntervalGood}
bind:value={$config.graduatingIntervalGood} /> bind:value={$config.graduatingIntervalGood}
/>
<SpinBox <SpinBox
label={tr.schedulingEasyInterval()} label={tr.schedulingEasyInterval()}
tooltip={tr.deckConfigEasyIntervalTooltip()} tooltip={tr.deckConfigEasyIntervalTooltip()}
warnings={[goodExceedsEasy]} warnings={[goodExceedsEasy]}
defaultValue={defaults.graduatingIntervalEasy} defaultValue={defaults.graduatingIntervalEasy}
bind:value={$config.graduatingIntervalEasy} /> bind:value={$config.graduatingIntervalEasy}
/>
<EnumSelector <EnumSelector
label={tr.deckConfigNewInsertionOrder()} label={tr.deckConfigNewInsertionOrder()}
tooltip={tr.deckConfigNewInsertionOrderTooltip()} tooltip={tr.deckConfigNewInsertionOrderTooltip()}
choices={newInsertOrderChoices} choices={newInsertOrderChoices}
defaultValue={defaults.newCardInsertOrder} defaultValue={defaults.newCardInsertOrder}
bind:value={$config.newCardInsertOrder} /> bind:value={$config.newCardInsertOrder}
/>

View File

@ -48,6 +48,17 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
} }
</script> </script>
{#if modified}
<div
class="img-div"
on:click={revert}
bind:this={ref}
title={tr.deckConfigRevertButtonTooltip()}
>
{@html revertIcon}
</div>
{/if}
<style lang="scss"> <style lang="scss">
.img-div { .img-div {
display: flex; display: flex;
@ -58,13 +69,3 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
} }
} }
</style> </style>
{#if modified}
<div
class="img-div"
on:click={revert}
bind:this={ref}
title={tr.deckConfigRevertButtonTooltip()}>
{@html revertIcon}
</div>
{/if}

View File

@ -56,8 +56,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
<WithDropdownMenu let:createDropdown let:activateDropdown let:menuId> <WithDropdownMenu let:createDropdown let:activateDropdown let:menuId>
<LabelButton on:mount={createDropdown} on:click={activateDropdown} /> <LabelButton on:mount={createDropdown} on:click={activateDropdown} />
<DropdownMenu id={menuId}> <DropdownMenu id={menuId}>
<DropdownItem on:click={() => dispatch('add')}>Add Config</DropdownItem> <DropdownItem on:click={() => dispatch("add")}>Add Config</DropdownItem>
<DropdownItem on:click={() => dispatch('rename')}> <DropdownItem on:click={() => dispatch("rename")}>
Rename Config Rename Config
</DropdownItem> </DropdownItem>
<DropdownItem on:click={removeConfig}>Remove Config</DropdownItem> <DropdownItem on:click={removeConfig}>Remove Config</DropdownItem>

View File

@ -29,5 +29,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
{max} {max}
bind:value bind:value
class="form-control" class="form-control"
on:blur={checkMinMax} /> on:blur={checkMinMax}
/>
</ConfigEntry> </ConfigEntry>

View File

@ -38,5 +38,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
step="0.01" step="0.01"
value={stringValue} value={stringValue}
on:blur={update} on:blur={update}
class="form-control" /> class="form-control"
/>
</ConfigEntry> </ConfigEntry>

View File

@ -36,6 +36,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
{defaultValue} {defaultValue}
{warnings} {warnings}
wholeLine={value.length > 2} wholeLine={value.length > 2}
on:revert={revert}> on:revert={revert}
>
<input type="text" value={stringValue} on:blur={update} class="form-control" /> <input type="text" value={stringValue} on:blur={update} class="form-control" />
</ConfigEntry> </ConfigEntry>

View File

@ -47,6 +47,53 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
const nightMode = getContext<boolean>(nightModeKey); const nightMode = getContext<boolean>(nightModeKey);
</script> </script>
<div
bind:this={modalRef}
class="modal fade"
tabindex="-1"
aria-labelledby="modalLabel"
aria-hidden="true"
>
<div class="modal-dialog">
<div class="modal-content" class:default-colors={nightMode}>
<div class="modal-header">
<h5 class="modal-title" id="modalLabel">{title}</h5>
<button
type="button"
class="btn-close"
class:invert={nightMode}
data-bs-dismiss="modal"
aria-label="Close"
/>
</div>
<div class="modal-body">
<form on:submit|preventDefault={onOkClicked}>
<div class="mb-3">
<label for="prompt-input" class="col-form-label"
>{prompt}:</label
>
<input
id="prompt-input"
bind:this={inputRef}
type="text"
class="form-control"
bind:value
/>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal"
>Cancel</button
>
<button type="button" class="btn btn-primary" on:click={onOkClicked}
>OK</button
>
</div>
</div>
</div>
</div>
<style lang="scss"> <style lang="scss">
.default-colors { .default-colors {
background-color: var(--window-bg); background-color: var(--window-bg);
@ -57,49 +104,3 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
filter: invert(1) grayscale(100%) brightness(200%); filter: invert(1) grayscale(100%) brightness(200%);
} }
</style> </style>
<div
bind:this={modalRef}
class="modal fade"
tabindex="-1"
aria-labelledby="modalLabel"
aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content" class:default-colors={nightMode}>
<div class="modal-header">
<h5 class="modal-title" id="modalLabel">{title}</h5>
<button
type="button"
class="btn-close"
class:invert={nightMode}
data-bs-dismiss="modal"
aria-label="Close" />
</div>
<div class="modal-body">
<form on:submit|preventDefault={onOkClicked}>
<div class="mb-3">
<label
for="prompt-input"
class="col-form-label">{prompt}:</label>
<input
id="prompt-input"
bind:this={inputRef}
type="text"
class="form-control"
bind:value />
</div>
</form>
</div>
<div class="modal-footer">
<button
type="button"
class="btn btn-secondary"
data-bs-dismiss="modal">Cancel</button>
<button
type="button"
class="btn btn-primary"
on:click={onOkClicked}>OK</button>
</div>
</div>
</div>
</div>

View File

@ -33,8 +33,7 @@ const exampleData = {
leechAction: "LEECH_ACTION_TAG_ONLY", leechAction: "LEECH_ACTION_TAG_ONLY",
leechThreshold: 8, leechThreshold: 8,
capAnswerTimeToSecs: 60, capAnswerTimeToSecs: 60,
other: other: "eyJuZXciOnsic2VwYXJhdGUiOnRydWV9LCJyZXYiOnsiZnV6eiI6MC4wNSwibWluU3BhY2UiOjF9fQ==",
"eyJuZXciOnsic2VwYXJhdGUiOnRydWV9LCJyZXYiOnsiZnV6eiI6MC4wNSwibWluU3BhY2UiOjF9fQ==",
}, },
}, },
useCount: 1, useCount: 1,

View File

@ -71,7 +71,8 @@ export class DeckOptionsState {
constructor(targetDeckId: number, data: pb.BackendProto.DeckConfigsForUpdate) { constructor(targetDeckId: number, data: pb.BackendProto.DeckConfigsForUpdate) {
this.targetDeckId = targetDeckId; this.targetDeckId = targetDeckId;
this.currentDeck = data.currentDeck as pb.BackendProto.DeckConfigsForUpdate.CurrentDeck; this.currentDeck =
data.currentDeck as pb.BackendProto.DeckConfigsForUpdate.CurrentDeck;
this.defaults = data.defaults!.config! as ConfigInner; this.defaults = data.defaults!.config! as ConfigInner;
this.configs = data.allConfig.map((config) => { this.configs = data.allConfig.map((config) => {
const configInner = config.config as pb.BackendProto.DeckConfig; const configInner = config.config as pb.BackendProto.DeckConfig;

View File

@ -41,11 +41,12 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
} }
</script> </script>
<WithShortcut shortcut={'Control+Alt?+Shift+C'} let:createShortcut let:shortcutLabel> <WithShortcut shortcut={"Control+Alt?+Shift+C"} let:createShortcut let:shortcutLabel>
<IconButton <IconButton
tooltip={`${tr.editingClozeDeletion()} (${shortcutLabel})`} tooltip={`${tr.editingClozeDeletion()} (${shortcutLabel})`}
on:click={onCloze} on:click={onCloze}
on:mount={createShortcut}> on:mount={createShortcut}
>
{@html bracketsIcon} {@html bracketsIcon}
</IconButton> </IconButton>
</WithShortcut> </WithShortcut>

View File

@ -36,23 +36,28 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
<ButtonGroup {api}> <ButtonGroup {api}>
<ButtonGroupItem> <ButtonGroupItem>
<WithShortcut shortcut={'F7'} let:createShortcut let:shortcutLabel> <WithShortcut shortcut={"F7"} let:createShortcut let:shortcutLabel>
<IconButton <IconButton
class="forecolor" class="forecolor"
tooltip={appendInParentheses(tr.editingSetForegroundColor(), shortcutLabel)} tooltip={appendInParentheses(
tr.editingSetForegroundColor(),
shortcutLabel
)}
on:click={wrapWithForecolor} on:click={wrapWithForecolor}
on:mount={createShortcut}> on:mount={createShortcut}
>
{@html squareFillIcon} {@html squareFillIcon}
</IconButton> </IconButton>
</WithShortcut> </WithShortcut>
</ButtonGroupItem> </ButtonGroupItem>
<ButtonGroupItem> <ButtonGroupItem>
<WithShortcut shortcut={'F8'} let:createShortcut let:shortcutLabel> <WithShortcut shortcut={"F8"} let:createShortcut let:shortcutLabel>
<ColorPicker <ColorPicker
tooltip={appendInParentheses(tr.editingChangeColor(), shortcutLabel)} tooltip={appendInParentheses(tr.editingChangeColor(), shortcutLabel)}
on:change={setWithCurrentColor} on:change={setWithCurrentColor}
on:mount={createShortcut} /> on:mount={createShortcut}
/>
</WithShortcut> </WithShortcut>
</ButtonGroupItem> </ButtonGroupItem>
</ButtonGroup> </ButtonGroup>

View File

@ -48,16 +48,18 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
<ButtonGroupItem> <ButtonGroupItem>
<WithState <WithState
key="insertUnorderedList" key="insertUnorderedList"
update={() => document.queryCommandState('insertUnorderedList')} update={() => document.queryCommandState("insertUnorderedList")}
let:state={active} let:state={active}
let:updateState> let:updateState
>
<IconButton <IconButton
tooltip={tr.editingUnorderedList()} tooltip={tr.editingUnorderedList()}
{active} {active}
on:click={(event) => { on:click={(event) => {
document.execCommand('insertUnorderedList'); document.execCommand("insertUnorderedList");
updateState(event); updateState(event);
}}> }}
>
{@html ulIcon} {@html ulIcon}
</IconButton> </IconButton>
</WithState> </WithState>
@ -66,16 +68,18 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
<ButtonGroupItem> <ButtonGroupItem>
<WithState <WithState
key="insertOrderedList" key="insertOrderedList"
update={() => document.queryCommandState('insertOrderedList')} update={() => document.queryCommandState("insertOrderedList")}
let:state={active} let:state={active}
let:updateState> let:updateState
>
<IconButton <IconButton
tooltip={tr.editingOrderedList()} tooltip={tr.editingOrderedList()}
{active} {active}
on:click={(event) => { on:click={(event) => {
document.execCommand('insertOrderedList'); document.execCommand("insertOrderedList");
updateState(event); updateState(event);
}}> }}
>
{@html olIcon} {@html olIcon}
</IconButton> </IconButton>
</WithState> </WithState>
@ -93,16 +97,18 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
<ButtonGroupItem> <ButtonGroupItem>
<WithState <WithState
key="justifyLeft" key="justifyLeft"
update={() => document.queryCommandState('justifyLeft')} update={() => document.queryCommandState("justifyLeft")}
let:state={active} let:state={active}
let:updateState> let:updateState
>
<IconButton <IconButton
tooltip={tr.editingAlignLeft()} tooltip={tr.editingAlignLeft()}
{active} {active}
on:click={(event) => { on:click={(event) => {
document.execCommand('justifyLeft'); document.execCommand("justifyLeft");
updateState(event); updateState(event);
}}> }}
>
{@html justifyLeftIcon} {@html justifyLeftIcon}
</IconButton> </IconButton>
</WithState> </WithState>
@ -111,16 +117,19 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
<ButtonGroupItem> <ButtonGroupItem>
<WithState <WithState
key="justifyCenter" key="justifyCenter"
update={() => document.queryCommandState('justifyCenter')} update={() =>
document.queryCommandState("justifyCenter")}
let:state={active} let:state={active}
let:updateState> let:updateState
>
<IconButton <IconButton
tooltip={tr.editingCenter()} tooltip={tr.editingCenter()}
{active} {active}
on:click={(event) => { on:click={(event) => {
document.execCommand('justifyCenter'); document.execCommand("justifyCenter");
updateState(event); updateState(event);
}}> }}
>
{@html justifyCenterIcon} {@html justifyCenterIcon}
</IconButton> </IconButton>
</WithState> </WithState>
@ -129,16 +138,19 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
<ButtonGroupItem> <ButtonGroupItem>
<WithState <WithState
key="justifyRight" key="justifyRight"
update={() => document.queryCommandState('justifyRight')} update={() =>
document.queryCommandState("justifyRight")}
let:state={active} let:state={active}
let:updateState> let:updateState
>
<IconButton <IconButton
tooltip={tr.editingAlignRight()} tooltip={tr.editingAlignRight()}
{active} {active}
on:click={(event) => { on:click={(event) => {
document.execCommand('justifyRight'); document.execCommand("justifyRight");
updateState(event); updateState(event);
}}> }}
>
{@html justifyRightIcon} {@html justifyRightIcon}
</IconButton> </IconButton>
</WithState> </WithState>
@ -147,16 +159,18 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
<ButtonGroupItem> <ButtonGroupItem>
<WithState <WithState
key="justifyFull" key="justifyFull"
update={() => document.queryCommandState('justifyFull')} update={() => document.queryCommandState("justifyFull")}
let:state={active} let:state={active}
let:updateState> let:updateState
>
<IconButton <IconButton
tooltip={tr.editingJustify()} tooltip={tr.editingJustify()}
{active} {active}
on:click={(event) => { on:click={(event) => {
document.execCommand('justifyFull'); document.execCommand("justifyFull");
updateState(event); updateState(event);
}}> }}
>
{@html justifyFullIcon} {@html justifyFullIcon}
</IconButton> </IconButton>
</WithState> </WithState>
@ -169,7 +183,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
<ButtonGroupItem> <ButtonGroupItem>
<IconButton <IconButton
on:click={outdentListItem} on:click={outdentListItem}
tooltip={tr.editingOutdent()}> tooltip={tr.editingOutdent()}
>
{@html outdentIcon} {@html outdentIcon}
</IconButton> </IconButton>
</ButtonGroupItem> </ButtonGroupItem>
@ -177,7 +192,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
<ButtonGroupItem> <ButtonGroupItem>
<IconButton <IconButton
on:click={indentListItem} on:click={indentListItem}
tooltip={tr.editingIndent()}> tooltip={tr.editingIndent()}
>
{@html indentIcon} {@html indentIcon}
</IconButton> </IconButton>
</ButtonGroupItem> </ButtonGroupItem>

View File

@ -26,20 +26,22 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
<ButtonGroup {api}> <ButtonGroup {api}>
<ButtonGroupItem> <ButtonGroupItem>
<WithShortcut shortcut={'Control+B'} let:createShortcut let:shortcutLabel> <WithShortcut shortcut={"Control+B"} let:createShortcut let:shortcutLabel>
<WithState <WithState
key="bold" key="bold"
update={() => document.queryCommandState('bold')} update={() => document.queryCommandState("bold")}
let:state={active} let:state={active}
let:updateState> let:updateState
>
<IconButton <IconButton
tooltip={appendInParentheses(tr.editingBoldText(), shortcutLabel)} tooltip={appendInParentheses(tr.editingBoldText(), shortcutLabel)}
{active} {active}
on:click={(event) => { on:click={(event) => {
document.execCommand('bold'); document.execCommand("bold");
updateState(event); updateState(event);
}} }}
on:mount={createShortcut}> on:mount={createShortcut}
>
{@html boldIcon} {@html boldIcon}
</IconButton> </IconButton>
</WithState> </WithState>
@ -47,20 +49,22 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
</ButtonGroupItem> </ButtonGroupItem>
<ButtonGroupItem> <ButtonGroupItem>
<WithShortcut shortcut={'Control+I'} let:createShortcut let:shortcutLabel> <WithShortcut shortcut={"Control+I"} let:createShortcut let:shortcutLabel>
<WithState <WithState
key="italic" key="italic"
update={() => document.queryCommandState('italic')} update={() => document.queryCommandState("italic")}
let:state={active} let:state={active}
let:updateState> let:updateState
>
<IconButton <IconButton
tooltip={appendInParentheses(tr.editingItalicText(), shortcutLabel)} tooltip={appendInParentheses(tr.editingItalicText(), shortcutLabel)}
{active} {active}
on:click={(event) => { on:click={(event) => {
document.execCommand('italic'); document.execCommand("italic");
updateState(event); updateState(event);
}} }}
on:mount={createShortcut}> on:mount={createShortcut}
>
{@html italicIcon} {@html italicIcon}
</IconButton> </IconButton>
</WithState> </WithState>
@ -68,20 +72,25 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
</ButtonGroupItem> </ButtonGroupItem>
<ButtonGroupItem> <ButtonGroupItem>
<WithShortcut shortcut={'Control+U'} let:createShortcut let:shortcutLabel> <WithShortcut shortcut={"Control+U"} let:createShortcut let:shortcutLabel>
<WithState <WithState
key="underline" key="underline"
update={() => document.queryCommandState('underline')} update={() => document.queryCommandState("underline")}
let:state={active} let:state={active}
let:updateState> let:updateState
>
<IconButton <IconButton
tooltip={appendInParentheses(tr.editingUnderlineText(), shortcutLabel)} tooltip={appendInParentheses(
tr.editingUnderlineText(),
shortcutLabel
)}
{active} {active}
on:click={(event) => { on:click={(event) => {
document.execCommand('underline'); document.execCommand("underline");
updateState(event); updateState(event);
}} }}
on:mount={createShortcut}> on:mount={createShortcut}
>
{@html underlineIcon} {@html underlineIcon}
</IconButton> </IconButton>
</WithState> </WithState>
@ -89,20 +98,25 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
</ButtonGroupItem> </ButtonGroupItem>
<ButtonGroupItem> <ButtonGroupItem>
<WithShortcut shortcut={'Control+='} let:createShortcut let:shortcutLabel> <WithShortcut shortcut={"Control+="} let:createShortcut let:shortcutLabel>
<WithState <WithState
key="superscript" key="superscript"
update={() => document.queryCommandState('superscript')} update={() => document.queryCommandState("superscript")}
let:state={active} let:state={active}
let:updateState> let:updateState
>
<IconButton <IconButton
tooltip={appendInParentheses(tr.editingSuperscript(), shortcutLabel)} tooltip={appendInParentheses(
tr.editingSuperscript(),
shortcutLabel
)}
{active} {active}
on:click={(event) => { on:click={(event) => {
document.execCommand('superscript'); document.execCommand("superscript");
updateState(event); updateState(event);
}} }}
on:mount={createShortcut}> on:mount={createShortcut}
>
{@html superscriptIcon} {@html superscriptIcon}
</IconButton> </IconButton>
</WithState> </WithState>
@ -110,20 +124,22 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
</ButtonGroupItem> </ButtonGroupItem>
<ButtonGroupItem> <ButtonGroupItem>
<WithShortcut shortcut={'Control+Shift+='} let:createShortcut let:shortcutLabel> <WithShortcut shortcut={"Control+Shift+="} let:createShortcut let:shortcutLabel>
<WithState <WithState
key="subscript" key="subscript"
update={() => document.queryCommandState('subscript')} update={() => document.queryCommandState("subscript")}
let:state={active} let:state={active}
let:updateState> let:updateState
>
<IconButton <IconButton
tooltip={appendInParentheses(tr.editingSubscript(), shortcutLabel)} tooltip={appendInParentheses(tr.editingSubscript(), shortcutLabel)}
{active} {active}
on:click={(event) => { on:click={(event) => {
document.execCommand('subscript'); document.execCommand("subscript");
updateState(event); updateState(event);
}} }}
on:mount={createShortcut}> on:mount={createShortcut}
>
{@html subscriptIcon} {@html subscriptIcon}
</IconButton> </IconButton>
</WithState> </WithState>
@ -131,13 +147,17 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
</ButtonGroupItem> </ButtonGroupItem>
<ButtonGroupItem> <ButtonGroupItem>
<WithShortcut shortcut={'Control+R'} let:createShortcut let:shortcutLabel> <WithShortcut shortcut={"Control+R"} let:createShortcut let:shortcutLabel>
<IconButton <IconButton
tooltip={appendInParentheses(tr.editingRemoveFormatting(), shortcutLabel)} tooltip={appendInParentheses(
tr.editingRemoveFormatting(),
shortcutLabel
)}
on:click={() => { on:click={() => {
document.execCommand('removeFormat'); document.execCommand("removeFormat");
}} }}
on:mount={createShortcut}> on:mount={createShortcut}
>
{@html eraserIcon} {@html eraserIcon}
</IconButton> </IconButton>
</WithShortcut> </WithShortcut>

View File

@ -19,18 +19,20 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
<LabelButton <LabelButton
disables={false} disables={false}
tooltip={tr.editingCustomizeFields()} tooltip={tr.editingCustomizeFields()}
on:click={() => bridgeCommand('fields')}> on:click={() => bridgeCommand("fields")}
>
{tr.editingFields()}... {tr.editingFields()}...
</LabelButton> </LabelButton>
</ButtonGroupItem> </ButtonGroupItem>
<ButtonGroupItem> <ButtonGroupItem>
<WithShortcut shortcut={'Control+L'} let:createShortcut let:shortcutLabel> <WithShortcut shortcut={"Control+L"} let:createShortcut let:shortcutLabel>
<LabelButton <LabelButton
disables={false} disables={false}
tooltip={`${tr.editingCustomizeCardTemplates()} (${shortcutLabel})`} tooltip={`${tr.editingCustomizeCardTemplates()} (${shortcutLabel})`}
on:click={() => bridgeCommand('cards')} on:click={() => bridgeCommand("cards")}
on:mount={createShortcut}> on:mount={createShortcut}
>
{tr.editingCards()}... {tr.editingCards()}...
</LabelButton> </LabelButton>
</WithShortcut> </WithShortcut>

View File

@ -10,12 +10,13 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import LabelButton from "components/LabelButton.svelte"; import LabelButton from "components/LabelButton.svelte";
</script> </script>
<WithShortcut shortcut={'Control+Shift+P'} let:createShortcut let:shortcutLabel> <WithShortcut shortcut={"Control+Shift+P"} let:createShortcut let:shortcutLabel>
<LabelButton <LabelButton
tooltip={tr.browsingPreviewSelectedCard({ val: shortcutLabel })} tooltip={tr.browsingPreviewSelectedCard({ val: shortcutLabel })}
disables={false} disables={false}
on:click={() => bridgeCommand('preview')} on:click={() => bridgeCommand("preview")}
on:mount={createShortcut}> on:mount={createShortcut}
>
{tr.actionsPreview()} {tr.actionsPreview()}
</LabelButton> </LabelButton>
</WithShortcut> </WithShortcut>

View File

@ -36,22 +36,27 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
<ButtonGroup {api}> <ButtonGroup {api}>
<ButtonGroupItem> <ButtonGroupItem>
<WithShortcut shortcut={'F3'} let:createShortcut let:shortcutLabel> <WithShortcut shortcut={"F3"} let:createShortcut let:shortcutLabel>
<IconButton <IconButton
tooltip={appendInParentheses(tr.editingAttachPicturesaudiovideo(), shortcutLabel)} tooltip={appendInParentheses(
tr.editingAttachPicturesaudiovideo(),
shortcutLabel
)}
on:click={onAttachment} on:click={onAttachment}
on:mount={createShortcut}> on:mount={createShortcut}
>
{@html paperclipIcon} {@html paperclipIcon}
</IconButton> </IconButton>
</WithShortcut> </WithShortcut>
</ButtonGroupItem> </ButtonGroupItem>
<ButtonGroupItem> <ButtonGroupItem>
<WithShortcut shortcut={'F5'} let:createShortcut let:shortcutLabel> <WithShortcut shortcut={"F5"} let:createShortcut let:shortcutLabel>
<IconButton <IconButton
tooltip={appendInParentheses(tr.editingRecordAudio(), shortcutLabel)} tooltip={appendInParentheses(tr.editingRecordAudio(), shortcutLabel)}
on:click={onRecord} on:click={onRecord}
on:mount={createShortcut}> on:mount={createShortcut}
>
{@html micIcon} {@html micIcon}
</IconButton> </IconButton>
</WithShortcut> </WithShortcut>
@ -69,72 +74,84 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
<DropdownMenu id={menuId}> <DropdownMenu id={menuId}>
<WithShortcut <WithShortcut
shortcut={'Control+M, M'} shortcut={"Control+M, M"}
let:createShortcut let:createShortcut
let:shortcutLabel> let:shortcutLabel
>
<DropdownItem <DropdownItem
on:click={() => wrap('\\(', '\\)')} on:click={() => wrap("\\(", "\\)")}
on:mount={createShortcut}> on:mount={createShortcut}
>
{tr.editingMathjaxInline()} {tr.editingMathjaxInline()}
<span class="ps-1 float-end">{shortcutLabel}</span> <span class="ps-1 float-end">{shortcutLabel}</span>
</DropdownItem> </DropdownItem>
</WithShortcut> </WithShortcut>
<WithShortcut <WithShortcut
shortcut={'Control+M, E'} shortcut={"Control+M, E"}
let:createShortcut let:createShortcut
let:shortcutLabel> let:shortcutLabel
>
<DropdownItem <DropdownItem
on:click={() => wrap('\\[', '\\]')} on:click={() => wrap("\\[", "\\]")}
on:mount={createShortcut}> on:mount={createShortcut}
>
{tr.editingMathjaxBlock()} {tr.editingMathjaxBlock()}
<span class="ps-1 float-end">{shortcutLabel}</span> <span class="ps-1 float-end">{shortcutLabel}</span>
</DropdownItem> </DropdownItem>
</WithShortcut> </WithShortcut>
<WithShortcut <WithShortcut
shortcut={'Control+M, C'} shortcut={"Control+M, C"}
let:createShortcut let:createShortcut
let:shortcutLabel> let:shortcutLabel
>
<DropdownItem <DropdownItem
on:click={() => wrap('\\(\\ce{', '}\\)')} on:click={() => wrap("\\(\\ce{", "}\\)")}
on:mount={createShortcut}> on:mount={createShortcut}
>
{tr.editingMathjaxChemistry()} {tr.editingMathjaxChemistry()}
<span class="ps-1 float-end">{shortcutLabel}</span> <span class="ps-1 float-end">{shortcutLabel}</span>
</DropdownItem> </DropdownItem>
</WithShortcut> </WithShortcut>
<WithShortcut <WithShortcut
shortcut={'Control+T, T'} shortcut={"Control+T, T"}
let:createShortcut let:createShortcut
let:shortcutLabel> let:shortcutLabel
>
<DropdownItem <DropdownItem
on:click={() => wrap('[latex]', '[/latex]')} on:click={() => wrap("[latex]", "[/latex]")}
on:mount={createShortcut}> on:mount={createShortcut}
>
{tr.editingLatex()} {tr.editingLatex()}
<span class="ps-1 float-end">{shortcutLabel}</span> <span class="ps-1 float-end">{shortcutLabel}</span>
</DropdownItem> </DropdownItem>
</WithShortcut> </WithShortcut>
<WithShortcut <WithShortcut
shortcut={'Control+T, E'} shortcut={"Control+T, E"}
let:createShortcut let:createShortcut
let:shortcutLabel> let:shortcutLabel
>
<DropdownItem <DropdownItem
on:click={() => wrap('[$]', '[/$]')} on:click={() => wrap("[$]", "[/$]")}
on:mount={createShortcut}> on:mount={createShortcut}
>
{tr.editingLatexEquation()} {tr.editingLatexEquation()}
<span class="ps-1 float-end">{shortcutLabel}</span> <span class="ps-1 float-end">{shortcutLabel}</span>
</DropdownItem> </DropdownItem>
</WithShortcut> </WithShortcut>
<WithShortcut <WithShortcut
shortcut={'Control+T, M'} shortcut={"Control+T, M"}
let:createShortcut let:createShortcut
let:shortcutLabel> let:shortcutLabel
>
<DropdownItem <DropdownItem
on:click={() => wrap('[$$]', '[/$$]')} on:click={() => wrap("[$$]", "[/$$]")}
on:mount={createShortcut}> on:mount={createShortcut}
>
{tr.editingLatexMathEnv()} {tr.editingLatexMathEnv()}
<span class="ps-1 float-end">{shortcutLabel}</span> <span class="ps-1 float-end">{shortcutLabel}</span>
</DropdownItem> </DropdownItem>
@ -144,11 +161,12 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
</ButtonGroupItem> </ButtonGroupItem>
<ButtonGroupItem> <ButtonGroupItem>
<WithShortcut shortcut={'Control+Shift+X'} let:createShortcut let:shortcutLabel> <WithShortcut shortcut={"Control+Shift+X"} let:createShortcut let:shortcutLabel>
<IconButton <IconButton
tooltip={appendInParentheses(tr.editingHtmlEditor(), shortcutLabel)} tooltip={appendInParentheses(tr.editingHtmlEditor(), shortcutLabel)}
on:click={onHtmlEdit} on:click={onHtmlEdit}
on:mount={createShortcut}> on:mount={createShortcut}
>
{@html xmlIcon} {@html xmlIcon}
</IconButton> </IconButton>
</WithShortcut> </WithShortcut>

View File

@ -78,25 +78,25 @@ export function caretToEnd(currentField: EditingArea): void {
selection.addRange(range); selection.addRange(range);
} }
const getAnchorParent = <T extends Element>( const getAnchorParent =
predicate: (element: Element) => element is T <T extends Element>(predicate: (element: Element) => element is T) =>
) => (currentField: DocumentOrShadowRoot): T | null => { (currentField: DocumentOrShadowRoot): T | null => {
const anchor = currentField.getSelection()?.anchorNode; const anchor = currentField.getSelection()?.anchorNode;
if (!anchor) { if (!anchor) {
return null; return null;
} }
let anchorParent: T | null = null; let anchorParent: T | null = null;
let element = nodeIsElement(anchor) ? anchor : anchor.parentElement; let element = nodeIsElement(anchor) ? anchor : anchor.parentElement;
while (element) { while (element) {
anchorParent = anchorParent || (predicate(element) ? element : null); anchorParent = anchorParent || (predicate(element) ? element : null);
element = element.parentElement; element = element.parentElement;
} }
return anchorParent; return anchorParent;
}; };
const isListItem = (element: Element): element is HTMLLIElement => const isListItem = (element: Element): element is HTMLLIElement =>
window.getComputedStyle(element).display === "list-item"; window.getComputedStyle(element).display === "list-item";

View File

@ -8,12 +8,12 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
export let bounds: GraphBounds; export let bounds: GraphBounds;
</script> </script>
<g class="x-ticks" transform={`translate(0, ${bounds.height - bounds.marginBottom})`} />
<g class="y-ticks" transform={`translate(${bounds.marginLeft}, 0)`} />
<g class="y2-ticks" transform={`translate(${bounds.width - bounds.marginRight}, 0)`} />
<style lang="scss"> <style lang="scss">
g :global(.domain) { g :global(.domain) {
opacity: 0.05; opacity: 0.05;
} }
</style> </style>
<g class="x-ticks" transform={`translate(0, ${bounds.height - bounds.marginBottom})`} />
<g class="y-ticks" transform={`translate(${bounds.marginLeft}, 0)`} />
<g class="y2-ticks" transform={`translate(${bounds.width - bounds.marginRight}, 0)`} />

View File

@ -28,8 +28,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
bounds.width = 225; bounds.width = 225;
bounds.marginBottom = 0; bounds.marginBottom = 0;
let graphData = (null as unknown) as GraphData; let graphData = null as unknown as GraphData;
let tableData = (null as unknown) as TableDatum[]; let tableData = null as unknown as TableDatum[];
$: { $: {
graphData = gatherData(sourceData, $cardCountsSeparateInactive); graphData = gatherData(sourceData, $cardCountsSeparateInactive);
@ -40,6 +40,52 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
const total = tr2.statisticsCountsTotalCards(); const total = tr2.statisticsCountsTotalCards();
</script> </script>
<Graph title={graphData.title}>
<InputBox>
<label>
<input type="checkbox" bind:checked={$cardCountsSeparateInactive} />
{label}
</label>
</InputBox>
<div class="counts-outer">
<svg
bind:this={svg}
viewBox={`0 0 ${bounds.width} ${bounds.height}`}
width={bounds.width}
height={bounds.height}
style="opacity: {graphData.totalCards ? 1 : 0}"
>
<g class="counts" />
</svg>
<div class="counts-table">
<table>
{#each tableData as d, _idx}
<tr>
<!-- prettier-ignore -->
<td>
<span style="color: {d.colour};">&nbsp;</span>
{#if browserLinksSupported}
<span class="search-link" on:click={() => dispatch('search', { query: d.query })}>{d.label}</span>
{:else}
<span>{d.label}</span>
{/if}
</td>
<td class="right">{d.count}</td>
<td class="right">{d.percent}</td>
</tr>
{/each}
<tr>
<td><span style="visibility: hidden;"></span> {total}</td>
<td class="right">{graphData.totalCards}</td>
<td />
</tr>
</table>
</div>
</div>
</Graph>
<style lang="scss"> <style lang="scss">
svg { svg {
transition: opacity 1s; transition: opacity 1s;
@ -74,48 +120,3 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
text-decoration: underline; text-decoration: underline;
} }
</style> </style>
<Graph title={graphData.title}>
<InputBox>
<label>
<input type="checkbox" bind:checked={$cardCountsSeparateInactive} />
{label}
</label>
</InputBox>
<div class="counts-outer">
<svg
bind:this={svg}
viewBox={`0 0 ${bounds.width} ${bounds.height}`}
width={bounds.width}
height={bounds.height}
style="opacity: {graphData.totalCards ? 1 : 0}">
<g class="counts" />
</svg>
<div class="counts-table">
<table>
{#each tableData as d, _idx}
<tr>
<!-- prettier-ignore -->
<td>
<span style="color: {d.colour};">&nbsp;</span>
{#if browserLinksSupported}
<span class="search-link" on:click={() => dispatch('search', { query: d.query })}>{d.label}</span>
{:else}
<span>{d.label}</span>
{/if}
</td>
<td class="right">{d.count}</td>
<td class="right">{d.percent}</td>
</tr>
{/each}
<tr>
<td><span style="visibility: hidden;"></span> {total}</td>
<td class="right">{graphData.totalCards}</td>
<td />
</tr>
</table>
</div>
</div>
</Graph>

View File

@ -1,3 +1,5 @@
<path class="cumulative-overlay" />
<!-- <!--
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
@ -25,5 +27,3 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
stroke-opacity: var(--area-stroke-opacity); stroke-opacity: var(--area-stroke-opacity);
} }
</style> </style>
<path class="cumulative-overlay" />

View File

@ -7,6 +7,16 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
export let subtitle: string | null = null; export let subtitle: string | null = null;
</script> </script>
<div class="graph" tabindex="-1">
<h1>{title}</h1>
{#if subtitle}
<div class="subtitle">{subtitle}</div>
{/if}
<slot />
</div>
<style lang="scss"> <style lang="scss">
.graph { .graph {
margin-left: auto; margin-left: auto;
@ -65,13 +75,3 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
margin-bottom: 1em; margin-bottom: 1em;
} }
</style> </style>
<div class="graph" tabindex="-1">
<h1>{title}</h1>
{#if subtitle}
<div class="subtitle">{subtitle}</div>
{/if}
<slot />
</div>

View File

@ -25,14 +25,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
} }
</script> </script>
<style lang="scss">
div {
@media only screen and (max-width: 600px) {
font-size: 12px;
}
}
</style>
<div> <div>
<WithGraphData <WithGraphData
{search} {search}
@ -40,7 +32,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
let:loading let:loading
let:sourceData let:sourceData
let:preferences let:preferences
let:revlogRange> let:revlogRange
>
{#if controller} {#if controller}
<svelte:component this={controller} {search} {days} {loading} /> <svelte:component this={controller} {search} {days} {loading} />
{/if} {/if}
@ -53,8 +46,17 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
{preferences} {preferences}
{revlogRange} {revlogRange}
{nightMode} {nightMode}
on:search={browserSearch} /> on:search={browserSearch}
/>
{/each} {/each}
{/if} {/if}
</WithGraphData> </WithGraphData>
</div> </div>
<style lang="scss">
div {
@media only screen and (max-width: 600px) {
font-size: 12px;
}
}
</style>

View File

@ -1,3 +1,5 @@
<g class="hover-columns" />
<!-- <!--
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
@ -15,5 +17,3 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
} }
} }
</style> </style>
<g class="hover-columns" />

View File

@ -1,3 +1,7 @@
<div>
<slot />
</div>
<!-- <!--
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
@ -17,7 +21,3 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
} }
} }
</style> </style>
<div>
<slot />
</div>

View File

@ -9,6 +9,11 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
const noData = tr.statisticsNoData(); const noData = tr.statisticsNoData();
</script> </script>
<g class="no-data">
<rect x="0" y="0" width={bounds.width} height={bounds.height} />
<text x="{bounds.width / 2}," y={bounds.height / 2}>{noData}</text>
</g>
<style lang="scss"> <style lang="scss">
.no-data { .no-data {
rect { rect {
@ -21,8 +26,3 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
} }
} }
</style> </style>
<g class="no-data">
<rect x="0" y="0" width={bounds.width} height={bounds.height} />
<text x="{bounds.width / 2}," y={bounds.height / 2}>{noData}</text>
</g>

View File

@ -67,6 +67,48 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
const all = tr.statisticsRangeAllHistory(); const all = tr.statisticsRangeAllHistory();
</script> </script>
<div class="range-box">
<div class="spin" class:loading></div>
<InputBox>
<label>
<input type="radio" bind:group={searchRange} value={SearchRange.Deck} />
{deck}
</label>
<label>
<input
type="radio"
bind:group={searchRange}
value={SearchRange.Collection}
/>
{collection}
</label>
<input
type="text"
bind:value={displayedSearch}
on:keyup={searchKeyUp}
on:focus={() => {
searchRange = SearchRange.Custom;
}}
placeholder={searchLabel}
/>
</InputBox>
<InputBox>
<label>
<input type="radio" bind:group={revlogRange} value={RevlogRange.Year} />
{year}
</label>
<label>
<input type="radio" bind:group={revlogRange} value={RevlogRange.All} />
{all}
</label>
</InputBox>
</div>
<div class="range-box-pad" />
<style lang="scss"> <style lang="scss">
.range-box { .range-box {
position: fixed; position: fixed;
@ -111,43 +153,3 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
height: 2em; height: 2em;
} }
</style> </style>
<div class="range-box">
<div class="spin" class:loading></div>
<InputBox>
<label>
<input type="radio" bind:group={searchRange} value={SearchRange.Deck} />
{deck}
</label>
<label>
<input
type="radio"
bind:group={searchRange}
value={SearchRange.Collection} />
{collection}
</label>
<input
type="text"
bind:value={displayedSearch}
on:keyup={searchKeyUp}
on:focus={() => {
searchRange = SearchRange.Custom;
}}
placeholder={searchLabel} />
</InputBox>
<InputBox>
<label>
<input type="radio" bind:group={revlogRange} value={RevlogRange.Year} />
{year}
</label>
<label>
<input type="radio" bind:group={revlogRange} value={RevlogRange.All} />
{all}
</label>
</InputBox>
</div>
<div class="range-box-pad" />

View File

@ -8,6 +8,17 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
export let tableData: TableDatum[]; export let tableData: TableDatum[];
</script> </script>
<div>
<table dir={i18n.direction()}>
{#each tableData as { label, value }}
<tr>
<td class="align-end">{label}:</td>
<td class="align-start">{value}</td>
</tr>
{/each}
</table>
</div>
<style lang="scss"> <style lang="scss">
div { div {
display: flex; display: flex;
@ -22,14 +33,3 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
text-align: start; text-align: start;
} }
</style> </style>
<div>
<table dir={i18n.direction()}>
{#each tableData as { label, value }}
<tr>
<td class="align-end">{label}:</td>
<td class="align-start">{value}</td>
</tr>
{/each}
</table>
</div>

View File

@ -18,12 +18,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
} }
</script> </script>
<style lang="scss">
.legend {
text-align: center;
}
</style>
{#if todayData} {#if todayData}
<Graph title={todayData.title}> <Graph title={todayData.title}>
<div class="legend"> <div class="legend">
@ -33,3 +27,9 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
</div> </div>
</Graph> </Graph>
{/if} {/if}
<style lang="scss">
.legend {
text-align: center;
}
</style>

View File

@ -8,7 +8,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
export let y: number = 0; export let y: number = 0;
export let show = true; export let show = true;
let container = (null as unknown) as HTMLDivElement; let container = null as unknown as HTMLDivElement;
let adjustedX: number, adjustedY: number; let adjustedX: number, adjustedY: number;
@ -24,6 +24,14 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
} }
</script> </script>
<div
bind:this={container}
class="tooltip"
style="left: {adjustedX}px; top: {adjustedY}px; opacity: {show ? 1 : 0}"
>
{@html html}
</div>
<style lang="scss"> <style lang="scss">
.tooltip { .tooltip {
position: absolute; position: absolute;
@ -41,10 +49,3 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
} }
} }
</style> </style>
<div
bind:this={container}
class="tooltip"
style="left: {adjustedX}px; top: {adjustedY}px; opacity: {show ? 1 : 0}">
{@html html}
</div>

View File

@ -84,4 +84,5 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
{revlogRange} {revlogRange}
loading={$graphLoading || $prefsLoading} loading={$graphLoading || $prefsLoading}
sourceData={$graphValue} sourceData={$graphValue}
preferences={$prefsValue} /> preferences={$prefsValue}
/>

View File

@ -158,13 +158,9 @@ export function histogramGraph(
); );
} }
const hoverData: [ const hoverData: [Bin<number, number>, number][] = data.bins.map(
Bin<number, number>, (bin: Bin<number, number>, index: number) => [bin, areaData[index + 1]]
number );
][] = data.bins.map((bin: Bin<number, number>, index: number) => [
bin,
areaData[index + 1],
]);
// hover/tooltip // hover/tooltip
const hoverzone = svg const hoverzone = svg

View File

@ -340,13 +340,9 @@ export function renderReviews(
); );
} }
const hoverData: [ const hoverData: [Bin<number, number>, number][] = bins.map(
Bin<number, number>, (bin: Bin<number, number>, index: number) => [bin, areaData[index + 1]]
number );
][] = bins.map((bin: Bin<number, number>, index: number) => [
bin,
areaData[index + 1],
]);
// hover/tooltip // hover/tooltip
svg.select("g.hover-columns") svg.select("g.hover-columns")

View File

@ -24,9 +24,8 @@ function showTooltipInner(msg: string, x: number, y: number): void {
tooltip.$set({ html: msg, x, y, show: true }); tooltip.$set({ html: msg, x, y, show: true });
} }
export const showTooltip: DebouncedFunc< export const showTooltip: DebouncedFunc<(msg: string, x: number, y: number) => void> =
(msg: string, x: number, y: number) => void throttle(showTooltipInner, 16);
> = throttle(showTooltipInner, 16);
export function hideTooltip(): void { export function hideTooltip(): void {
const tooltip = getOrCreateTooltip(); const tooltip = getOrCreateTooltip();

View File

@ -32,8 +32,13 @@ function allowNone(element: Element): void {
filterAttributes(() => false, element); filterAttributes(() => false, element);
} }
const allow = (attrs: string[]): FilterMethod => (element: Element): void => const allow =
filterAttributes((attributeName: string) => attrs.includes(attributeName), element); (attrs: string[]): FilterMethod =>
(element: Element): void =>
filterAttributes(
(attributeName: string) => attrs.includes(attributeName),
element
);
function unwrapElement(element: Element): void { function unwrapElement(element: Element): void {
element.replaceWith(...element.childNodes); element.replaceWith(...element.childNodes);
@ -88,19 +93,19 @@ const tagsAllowedExtended: TagsAllowed = {
UL: allowNone, UL: allowNone,
}; };
const filterElementTagsAllowed = (tagsAllowed: TagsAllowed) => ( const filterElementTagsAllowed =
element: Element (tagsAllowed: TagsAllowed) =>
): void => { (element: Element): void => {
const tagName = element.tagName; const tagName = element.tagName;
if (Object.prototype.hasOwnProperty.call(tagsAllowed, tagName)) { if (Object.prototype.hasOwnProperty.call(tagsAllowed, tagName)) {
tagsAllowed[tagName](element); tagsAllowed[tagName](element);
} else if (element.innerHTML) { } else if (element.innerHTML) {
unwrapElement(element); unwrapElement(element);
} else { } else {
removeElement(element); removeElement(element);
} }
}; };
export const filterElementBasic = filterElementTagsAllowed(tagsAllowedBasic); export const filterElementBasic = filterElementTagsAllowed(tagsAllowedBasic);
export const filterElementExtended = filterElementTagsAllowed(tagsAllowedExtended); export const filterElementExtended = filterElementTagsAllowed(tagsAllowedExtended);

View File

@ -14,24 +14,24 @@ function iterateElement(
} }
} }
export const filterNode = (elementFilter: (element: Element) => void) => ( export const filterNode =
node: Node (elementFilter: (element: Element) => void) =>
): void => { (node: Node): void => {
switch (node.nodeType) { switch (node.nodeType) {
case Node.COMMENT_NODE: case Node.COMMENT_NODE:
removeNode(node); removeNode(node);
break; break;
case Node.DOCUMENT_FRAGMENT_NODE: case Node.DOCUMENT_FRAGMENT_NODE:
iterateElement(filterNode(elementFilter), node as DocumentFragment); iterateElement(filterNode(elementFilter), node as DocumentFragment);
break; break;
case Node.ELEMENT_NODE: case Node.ELEMENT_NODE:
iterateElement(filterNode(elementFilter), node as Element); iterateElement(filterNode(elementFilter), node as Element);
elementFilter(node as Element); elementFilter(node as Element);
break; break;
default: default:
// do nothing // do nothing
} }
}; };

View File

@ -27,27 +27,28 @@ const stylingInternal: BlockProperties = [
"font-family", "font-family",
]; ];
const allowPropertiesBlockValues = ( const allowPropertiesBlockValues =
allowBlock: AllowPropertiesBlockValues (allowBlock: AllowPropertiesBlockValues): StylingPredicate =>
): StylingPredicate => (property: string, value: string): boolean => (property: string, value: string): boolean =>
Object.prototype.hasOwnProperty.call(allowBlock, property) && Object.prototype.hasOwnProperty.call(allowBlock, property) &&
!allowBlock[property].includes(value); !allowBlock[property].includes(value);
const blockProperties = (block: BlockProperties): StylingPredicate => ( const blockProperties =
property: string (block: BlockProperties): StylingPredicate =>
): boolean => !block.includes(property); (property: string): boolean =>
!block.includes(property);
const filterStyling = (predicate: (property: string, value: string) => boolean) => ( const filterStyling =
element: HTMLElement (predicate: (property: string, value: string) => boolean) =>
): void => { (element: HTMLElement): void => {
for (const property of [...element.style]) { for (const property of [...element.style]) {
const value = element.style.getPropertyValue(property); const value = element.style.getPropertyValue(property);
if (!predicate(property, value)) { if (!predicate(property, value)) {
element.style.removeProperty(property); element.style.removeProperty(property);
}
} }
} };
};
export const filterStylingNightMode = filterStyling( export const filterStylingNightMode = filterStyling(
allowPropertiesBlockValues(stylingNightMode) allowPropertiesBlockValues(stylingNightMode)

View File

@ -122,11 +122,11 @@ function checkModifiers(event: KeyboardEvent, modifiers: string[]): boolean {
); );
} }
const check = (keyCode: number, modifiers: string[]) => ( const check =
event: KeyboardEvent (keyCode: number, modifiers: string[]) =>
): boolean => { (event: KeyboardEvent): boolean => {
return checkKey(event, keyCode) && checkModifiers(event, modifiers); return checkKey(event, keyCode) && checkModifiers(event, modifiers);
}; };
function keyToCode(key: string): number { function keyToCode(key: string): number {
return keyCodeLookup[key] || key.toUpperCase().charCodeAt(0); return keyCodeLookup[key] || key.toUpperCase().charCodeAt(0);
@ -176,9 +176,8 @@ export function registerShortcut(
callback: (event: KeyboardEvent) => void, callback: (event: KeyboardEvent) => void,
keyCombinationString: string keyCombinationString: string
): () => void { ): () => void {
const [check, ...restChecks] = splitKeyCombinationString(keyCombinationString).map( const [check, ...restChecks] =
keyCombinationToCheck splitKeyCombinationString(keyCombinationString).map(keyCombinationToCheck);
);
const handler = (event: KeyboardEvent): void => { const handler = (event: KeyboardEvent): void => {
if (check(event)) { if (check(event)) {

View File

@ -108,7 +108,7 @@
"path": "node_modules/@types/lodash-es", "path": "node_modules/@types/lodash-es",
"licenseFile": "node_modules/@types/lodash-es/LICENSE" "licenseFile": "node_modules/@types/lodash-es/LICENSE"
}, },
"@types/lodash@4.14.168": { "@types/lodash@4.14.170": {
"licenses": "MIT", "licenses": "MIT",
"repository": "https://github.com/DefinitelyTyped/DefinitelyTyped", "repository": "https://github.com/DefinitelyTyped/DefinitelyTyped",
"path": "node_modules/@types/lodash", "path": "node_modules/@types/lodash",
@ -120,19 +120,19 @@
"path": "node_modules/@types/long", "path": "node_modules/@types/long",
"licenseFile": "node_modules/@types/long/LICENSE" "licenseFile": "node_modules/@types/long/LICENSE"
}, },
"@types/marked@2.0.2": { "@types/marked@2.0.3": {
"licenses": "MIT", "licenses": "MIT",
"repository": "https://github.com/DefinitelyTyped/DefinitelyTyped", "repository": "https://github.com/DefinitelyTyped/DefinitelyTyped",
"path": "node_modules/@types/marked", "path": "node_modules/@types/marked",
"licenseFile": "node_modules/@types/marked/LICENSE" "licenseFile": "node_modules/@types/marked/LICENSE"
}, },
"@types/node@15.0.2": { "@types/node@15.6.1": {
"licenses": "MIT", "licenses": "MIT",
"repository": "https://github.com/DefinitelyTyped/DefinitelyTyped", "repository": "https://github.com/DefinitelyTyped/DefinitelyTyped",
"path": "node_modules/protobufjs/node_modules/@types/node", "path": "node_modules/protobufjs/node_modules/@types/node",
"licenseFile": "node_modules/protobufjs/node_modules/@types/node/LICENSE" "licenseFile": "node_modules/protobufjs/node_modules/@types/node/LICENSE"
}, },
"bootstrap-icons@1.4.1": { "bootstrap-icons@1.5.0": {
"licenses": "MIT", "licenses": "MIT",
"repository": "https://github.com/twbs/icons", "repository": "https://github.com/twbs/icons",
"publisher": "mdo", "publisher": "mdo",
@ -473,7 +473,7 @@
"path": "node_modules/long", "path": "node_modules/long",
"licenseFile": "node_modules/long/LICENSE" "licenseFile": "node_modules/long/LICENSE"
}, },
"marked@2.0.3": { "marked@2.0.5": {
"licenses": "MIT", "licenses": "MIT",
"repository": "https://github.com/markedjs/marked", "repository": "https://github.com/markedjs/marked",
"publisher": "Christopher Jeffrey", "publisher": "Christopher Jeffrey",

View File

@ -36,8 +36,8 @@
"jsdoc": "^3.6.6", "jsdoc": "^3.6.6",
"license-checker-rseidelsohn": "^1.2.2", "license-checker-rseidelsohn": "^1.2.2",
"minimist": "^1.2.5", "minimist": "^1.2.5",
"prettier": "^2.1.2", "prettier": "=2.3.0",
"prettier-plugin-svelte": "^1.4.0", "prettier-plugin-svelte": "=2.3.0",
"sass": "^1.32.6", "sass": "^1.32.6",
"semver": "^7.3.4", "semver": "^7.3.4",
"svelte": "^3.25.0", "svelte": "^3.25.0",

View File

@ -9,11 +9,13 @@ export interface DynamicSvelteComponent<
[k: string]: unknown; [k: string]: unknown;
} }
export const dynamicComponent = < export const dynamicComponent =
Comp extends typeof SvelteComponentDev, <
DefaultProps = NonNullable<ConstructorParameters<Comp>[0]["props"]> Comp extends typeof SvelteComponentDev,
>( DefaultProps = NonNullable<ConstructorParameters<Comp>[0]["props"]>
component: Comp >(
) => <Props = DefaultProps>(props: Props): DynamicSvelteComponent<Comp> & Props => { component: Comp
return { component, ...props }; ) =>
}; <Props = DefaultProps>(props: Props): DynamicSvelteComponent<Comp> & Props => {
return { component, ...props };
};

File diff suppressed because it is too large Load Diff