anki/ts/components/Select.svelte

139 lines
3.4 KiB
Svelte
Raw Normal View History

<!--
Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
<script lang="ts">
import { createEventDispatcher, setContext } from "svelte";
import { writable } from "svelte/store";
import { selectKey } from "./context-keys";
import IconConstrain from "./IconConstrain.svelte";
import { chevronDown } from "./icons";
import Popover from "./Popover.svelte";
import WithFloating from "./WithFloating.svelte";
Migrate to protobuf-es (#2547) * Fix .no-reduce-motion missing from graphs spinner, and not being honored * Begin migration from protobuf.js -> protobuf-es Motivation: - Protobuf-es has a nicer API: messages are represented as classes, and fields which should exist are not marked as nullable. - As it uses modules, only the proto messages we actually use get included in our bundle output. Protobuf.js put everything in a namespace, which prevented tree-shaking, and made it awkward to access inner messages. - ./run after touching a proto file drops from about 8s to 6s on my machine. The tradeoff is slower decoding/encoding (#2043), but that was mainly a concern for the graphs page, and was unblocked by https://github.com/ankitects/anki/commit/37151213cd9d431f449ba4b3bc4c0329a1d9af78 Approach/notes: - We generate the new protobuf-es interface in addition to existing protobuf.js interface, so we can migrate a module at a time, starting with the graphs module. - rslib:proto now generates RPC methods for TS in addition to the Python interface. The input-arg-unrolling behaviour of the Python generation is not required here, as we declare the input arg as a PlainMessage<T>, which marks it as requiring all fields to be provided. - i64 is represented as bigint in protobuf-es. We were using a patch to protobuf.js to get it to output Javascript numbers instead of long.js types, but now that our supported browser versions support bigint, it's probably worth biting the bullet and migrating to bigint use. Our IDs fit comfortably within MAX_SAFE_INTEGER, but that may not hold for future fields we add. - Oneofs are handled differently in protobuf-es, and are going to need some refactoring. Other notable changes: - Added a --mkdir arg to our build runner, so we can create a dir easily during the build on Windows. - Simplified the preference handling code, by wrapping the preferences in an outer store, instead of a separate store for each individual preference. This means a change to one preference will trigger a redraw of all components that depend on the preference store, but the redrawing is cheap after moving the data processing to Rust, and it makes the code easier to follow. - Drop async(Reactive).ts in favour of more explicit handling with await blocks/updating. - Renamed add_inputs_to_group() -> add_dependency(), and fixed it not adding dependencies to parent groups. Renamed add() -> add_action() for clarity. * Remove a couple of unused proto imports * Migrate card info * Migrate congrats, image occlusion, and tag editor + Fix imports for multi-word proto files. * Migrate change-notetype * Migrate deck options * Bump target to es2020; simplify ts lib list Have used caniuse.com to confirm Chromium 77, iOS 14.5 and the Chrome on Android support the full es2017-es2020 features. * Migrate import-csv * Migrate i18n and fix missing output types in .js * Migrate custom scheduling, and remove protobuf.js To mostly maintain our old API contract, we make use of protobuf-es's ability to convert to JSON, which follows the same format as protobuf.js did. It doesn't cover all case: users who were previously changing the variant of a type will need to update their code, as assigning to a new variant no longer automatically removes the old one, which will cause an error when we try to convert back from JSON. But I suspect the large majority of users are adjusting the current variant rather than creating a new one, and this saves us having to write proxy wrappers, so it seems like a reasonable compromise. One other change I made at the same time was to rename value->kind for the oneofs in our custom study protos, as 'value' was easily confused with the 'case/value' output that protobuf-es has. With protobuf.js codegen removed, touching a proto file and invoking ./run drops from about 8s to 6s. This closes #2043. * Allow tree-shaking on protobuf types * Display backend error messages in our ts alert() * Make sourcemap generation opt-in for ts-run Considerably slows down build, and not used most of the time.
2023-06-14 14:47:37 +02:00
type T = $$Generic;
export let id: string | undefined = undefined;
let className = "";
export { className as class };
export let disabled = false;
export let label = "<br>";
Migrate to protobuf-es (#2547) * Fix .no-reduce-motion missing from graphs spinner, and not being honored * Begin migration from protobuf.js -> protobuf-es Motivation: - Protobuf-es has a nicer API: messages are represented as classes, and fields which should exist are not marked as nullable. - As it uses modules, only the proto messages we actually use get included in our bundle output. Protobuf.js put everything in a namespace, which prevented tree-shaking, and made it awkward to access inner messages. - ./run after touching a proto file drops from about 8s to 6s on my machine. The tradeoff is slower decoding/encoding (#2043), but that was mainly a concern for the graphs page, and was unblocked by https://github.com/ankitects/anki/commit/37151213cd9d431f449ba4b3bc4c0329a1d9af78 Approach/notes: - We generate the new protobuf-es interface in addition to existing protobuf.js interface, so we can migrate a module at a time, starting with the graphs module. - rslib:proto now generates RPC methods for TS in addition to the Python interface. The input-arg-unrolling behaviour of the Python generation is not required here, as we declare the input arg as a PlainMessage<T>, which marks it as requiring all fields to be provided. - i64 is represented as bigint in protobuf-es. We were using a patch to protobuf.js to get it to output Javascript numbers instead of long.js types, but now that our supported browser versions support bigint, it's probably worth biting the bullet and migrating to bigint use. Our IDs fit comfortably within MAX_SAFE_INTEGER, but that may not hold for future fields we add. - Oneofs are handled differently in protobuf-es, and are going to need some refactoring. Other notable changes: - Added a --mkdir arg to our build runner, so we can create a dir easily during the build on Windows. - Simplified the preference handling code, by wrapping the preferences in an outer store, instead of a separate store for each individual preference. This means a change to one preference will trigger a redraw of all components that depend on the preference store, but the redrawing is cheap after moving the data processing to Rust, and it makes the code easier to follow. - Drop async(Reactive).ts in favour of more explicit handling with await blocks/updating. - Renamed add_inputs_to_group() -> add_dependency(), and fixed it not adding dependencies to parent groups. Renamed add() -> add_action() for clarity. * Remove a couple of unused proto imports * Migrate card info * Migrate congrats, image occlusion, and tag editor + Fix imports for multi-word proto files. * Migrate change-notetype * Migrate deck options * Bump target to es2020; simplify ts lib list Have used caniuse.com to confirm Chromium 77, iOS 14.5 and the Chrome on Android support the full es2017-es2020 features. * Migrate import-csv * Migrate i18n and fix missing output types in .js * Migrate custom scheduling, and remove protobuf.js To mostly maintain our old API contract, we make use of protobuf-es's ability to convert to JSON, which follows the same format as protobuf.js did. It doesn't cover all case: users who were previously changing the variant of a type will need to update their code, as assigning to a new variant no longer automatically removes the old one, which will cause an error when we try to convert back from JSON. But I suspect the large majority of users are adjusting the current variant rather than creating a new one, and this saves us having to write proxy wrappers, so it seems like a reasonable compromise. One other change I made at the same time was to rename value->kind for the oneofs in our custom study protos, as 'value' was easily confused with the 'case/value' output that protobuf-es has. With protobuf.js codegen removed, touching a proto file and invoking ./run drops from about 8s to 6s. This closes #2043. * Allow tree-shaking on protobuf types * Display backend error messages in our ts alert() * Make sourcemap generation opt-in for ts-run Considerably slows down build, and not used most of the time.
2023-06-14 14:47:37 +02:00
export let value: T;
const dispatch = createEventDispatcher();
Migrate to protobuf-es (#2547) * Fix .no-reduce-motion missing from graphs spinner, and not being honored * Begin migration from protobuf.js -> protobuf-es Motivation: - Protobuf-es has a nicer API: messages are represented as classes, and fields which should exist are not marked as nullable. - As it uses modules, only the proto messages we actually use get included in our bundle output. Protobuf.js put everything in a namespace, which prevented tree-shaking, and made it awkward to access inner messages. - ./run after touching a proto file drops from about 8s to 6s on my machine. The tradeoff is slower decoding/encoding (#2043), but that was mainly a concern for the graphs page, and was unblocked by https://github.com/ankitects/anki/commit/37151213cd9d431f449ba4b3bc4c0329a1d9af78 Approach/notes: - We generate the new protobuf-es interface in addition to existing protobuf.js interface, so we can migrate a module at a time, starting with the graphs module. - rslib:proto now generates RPC methods for TS in addition to the Python interface. The input-arg-unrolling behaviour of the Python generation is not required here, as we declare the input arg as a PlainMessage<T>, which marks it as requiring all fields to be provided. - i64 is represented as bigint in protobuf-es. We were using a patch to protobuf.js to get it to output Javascript numbers instead of long.js types, but now that our supported browser versions support bigint, it's probably worth biting the bullet and migrating to bigint use. Our IDs fit comfortably within MAX_SAFE_INTEGER, but that may not hold for future fields we add. - Oneofs are handled differently in protobuf-es, and are going to need some refactoring. Other notable changes: - Added a --mkdir arg to our build runner, so we can create a dir easily during the build on Windows. - Simplified the preference handling code, by wrapping the preferences in an outer store, instead of a separate store for each individual preference. This means a change to one preference will trigger a redraw of all components that depend on the preference store, but the redrawing is cheap after moving the data processing to Rust, and it makes the code easier to follow. - Drop async(Reactive).ts in favour of more explicit handling with await blocks/updating. - Renamed add_inputs_to_group() -> add_dependency(), and fixed it not adding dependencies to parent groups. Renamed add() -> add_action() for clarity. * Remove a couple of unused proto imports * Migrate card info * Migrate congrats, image occlusion, and tag editor + Fix imports for multi-word proto files. * Migrate change-notetype * Migrate deck options * Bump target to es2020; simplify ts lib list Have used caniuse.com to confirm Chromium 77, iOS 14.5 and the Chrome on Android support the full es2017-es2020 features. * Migrate import-csv * Migrate i18n and fix missing output types in .js * Migrate custom scheduling, and remove protobuf.js To mostly maintain our old API contract, we make use of protobuf-es's ability to convert to JSON, which follows the same format as protobuf.js did. It doesn't cover all case: users who were previously changing the variant of a type will need to update their code, as assigning to a new variant no longer automatically removes the old one, which will cause an error when we try to convert back from JSON. But I suspect the large majority of users are adjusting the current variant rather than creating a new one, and this saves us having to write proxy wrappers, so it seems like a reasonable compromise. One other change I made at the same time was to rename value->kind for the oneofs in our custom study protos, as 'value' was easily confused with the 'case/value' output that protobuf-es has. With protobuf.js codegen removed, touching a proto file and invoking ./run drops from about 8s to 6s. This closes #2043. * Allow tree-shaking on protobuf types * Display backend error messages in our ts alert() * Make sourcemap generation opt-in for ts-run Considerably slows down build, and not used most of the time.
2023-06-14 14:47:37 +02:00
function setValue(v: T) {
value = v;
dispatch("change", { value });
}
export let element: HTMLElement | undefined = undefined;
export let tooltip: string | undefined = undefined;
const rtl: boolean = window.getComputedStyle(document.body).direction == "rtl";
let hover = false;
let showFloating = false;
let clientWidth: number;
async function handleKey(e: KeyboardEvent) {
if (e.code === "Enter") {
e.preventDefault();
showFloating = !showFloating;
}
}
const selectStore = writable({ value, setValue });
$: $selectStore.value = value;
setContext(selectKey, selectStore);
</script>
<WithFloating
show={showFloating}
offset={0}
shift={0}
hideArrow
inline
closeOnInsideClick
keepOnKeyup
on:close={() => (showFloating = false)}
let:asReference
>
<div
{id}
class="{className} select-container"
class:rtl
class:hover
{disabled}
title={tooltip}
tabindex="0"
on:keypress={handleKey}
on:mouseenter={() => (hover = true)}
on:mouseleave={() => (hover = false)}
on:click={() => (showFloating = !showFloating)}
bind:this={element}
use:asReference
bind:clientWidth
>
<div class="inner">
<div class="label">{@html label}</div>
</div>
<div class="chevron">
<IconConstrain iconSize={80}>
{@html chevronDown}
</IconConstrain>
</div>
</div>
<Popover slot="floating" scrollable --popover-width="{clientWidth}px">
<slot />
</Popover>
</WithFloating>
<style lang="scss">
@use "sass/button-mixins" as button;
$padding-inline: 0.5rem;
.select-container {
@include button.select($with-disabled: false);
line-height: 1.5;
height: 100%;
position: relative;
display: flex;
flex-flow: row;
justify-content: space-between;
.inner {
flex-grow: 1;
position: relative;
.label {
position: absolute;
top: 0;
right: $padding-inline;
bottom: 0;
left: $padding-inline;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
}
.chevron {
height: 100%;
align-self: flex-end;
Add comments to Sass variables and tweak main window (#2137) * Prevent multiple inclusion of variables in CSS files * Use dict instead of tuple for variables * Add comments to variables * Improve appearance of main window * Tweak main window styles * Use json.dumps over pprint.format * Make study button primary * Improve header margin * Make bottom toolbar slimmer * Make congrats page more balanced * Fix type issue * Replace day/night with light/dark * Exclude top-level-drag-row from hover effect * Create dataclass for variables * Run formatter * Apply CSS variables from Python side Why go full-circle with the Sass variables? This way we only need one interface for add-on authors to interact with. It also makes it easier for us to apply additional themes in the future. * Fix typing * Fix rgba values in Qt * Darken button background * Fix palette not being applied in light theme For some odd reason this problem arose much later than #2016. * Tweak default button look * Reformat * Apply CSS vars to ts pages * Include elevation in button_mixins_lib * Cast opacity to int * Add some margin to studiedToday info * Tweak light theme button gradient * Tweak highlight-bg for light theme * Add back default button color as it made the browser sidebar tool icons dark in light theme. * Reformat * Tweak light theme buttons once more Sorry for the back-and-forth. Sass only compiles when there are changes in user files, not when I only change the vars. * Fix bottom toolbar button indicators * Make buttons more clicky * Fix button padding * Handle macOS separately again * Decrease elevation effect for main window buttons to 1 * Imitate box-shadow for Qt elements * Adjust shadow vars * Adjust primary border color because the save button in the deck options had a lighter color than its background gradient. * Boost box-shadow color of primary buttons * Format * Adjust Qt box-shadow imitation and shadow colors * Use more subtle default shadow color * Add some more padding to top toolbar * Revert "Apply CSS vars to ts pages" This reverts commit 5d8e7f6b7ffc8894b6517ecbb8cfba35407fc69a. * Revert "Apply CSS variables from Python side" This reverts commit 87db774412fd2bfd75e2630d2c5e782daef96b5f. * Better match the standard macOS buttons In the dark theme the standard color is a lighter grey, but at least the size/shape is similar again. This doesn't work for the editor buttons. * Reduce the top margin of the congrats screen * Fix illegible buttons when changing theme on macOS; match dark button style
2022-10-29 02:48:53 +02:00
border-left: 1px solid var(--border-subtle);
}
:global([dir="rtl"]) {
.chevron {
border-left: none;
Add comments to Sass variables and tweak main window (#2137) * Prevent multiple inclusion of variables in CSS files * Use dict instead of tuple for variables * Add comments to variables * Improve appearance of main window * Tweak main window styles * Use json.dumps over pprint.format * Make study button primary * Improve header margin * Make bottom toolbar slimmer * Make congrats page more balanced * Fix type issue * Replace day/night with light/dark * Exclude top-level-drag-row from hover effect * Create dataclass for variables * Run formatter * Apply CSS variables from Python side Why go full-circle with the Sass variables? This way we only need one interface for add-on authors to interact with. It also makes it easier for us to apply additional themes in the future. * Fix typing * Fix rgba values in Qt * Darken button background * Fix palette not being applied in light theme For some odd reason this problem arose much later than #2016. * Tweak default button look * Reformat * Apply CSS vars to ts pages * Include elevation in button_mixins_lib * Cast opacity to int * Add some margin to studiedToday info * Tweak light theme button gradient * Tweak highlight-bg for light theme * Add back default button color as it made the browser sidebar tool icons dark in light theme. * Reformat * Tweak light theme buttons once more Sorry for the back-and-forth. Sass only compiles when there are changes in user files, not when I only change the vars. * Fix bottom toolbar button indicators * Make buttons more clicky * Fix button padding * Handle macOS separately again * Decrease elevation effect for main window buttons to 1 * Imitate box-shadow for Qt elements * Adjust shadow vars * Adjust primary border color because the save button in the deck options had a lighter color than its background gradient. * Boost box-shadow color of primary buttons * Format * Adjust Qt box-shadow imitation and shadow colors * Use more subtle default shadow color * Add some more padding to top toolbar * Revert "Apply CSS vars to ts pages" This reverts commit 5d8e7f6b7ffc8894b6517ecbb8cfba35407fc69a. * Revert "Apply CSS variables from Python side" This reverts commit 87db774412fd2bfd75e2630d2c5e782daef96b5f. * Better match the standard macOS buttons In the dark theme the standard color is a lighter grey, but at least the size/shape is similar again. This doesn't work for the editor buttons. * Reduce the top margin of the congrats screen * Fix illegible buttons when changing theme on macOS; match dark button style
2022-10-29 02:48:53 +02:00
border-right: 1px solid var(--border-subtle);
}
}
</style>