mirror of
https://github.com/ankitects/anki.git
synced 2025-09-23 16:26:40 -04:00
Use TextInputModal directly from svelte component
* the only important thing is that it is not positioned within elements with display: none * I think we can treat the existence of the modal to be a kind of "precondition" that has to be met for the component to be used
This commit is contained in:
parent
412091ae60
commit
5cc6fc7d9b
8 changed files with 76 additions and 102 deletions
|
@ -6,3 +6,4 @@ export const disabledKey = Symbol("disabled");
|
|||
export const buttonToolbarKey = Symbol("buttonToolbar");
|
||||
export const buttonGroupKey = Symbol("buttonGroup");
|
||||
export const dropdownKey = Symbol("dropdown");
|
||||
export const modalsKey = Symbol("modals");
|
||||
|
|
|
@ -61,11 +61,9 @@ ts_library(
|
|||
"lib.ts",
|
||||
"steps.ts",
|
||||
"strings.ts",
|
||||
"textInputModal.ts",
|
||||
],
|
||||
module_name = "deckoptions",
|
||||
deps = [
|
||||
"TextInputModal",
|
||||
"//ts:image_module_support",
|
||||
"//ts/lib",
|
||||
"//ts/lib:backend_proto",
|
||||
|
|
|
@ -6,8 +6,9 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
import * as tr from "lib/i18n";
|
||||
import type { DeckOptionsState, ConfigListEntry } from "./lib";
|
||||
|
||||
import WithTheming from "components/WithTheming.svelte";
|
||||
import TextInputModal from "./TextInputModal.svelte";
|
||||
import StickyBar from "components/StickyBar.svelte";
|
||||
import WithTheming from "components/WithTheming.svelte";
|
||||
import ButtonToolbar from "components/ButtonToolbar.svelte";
|
||||
import ButtonToolbarItem from "components/ButtonToolbarItem.svelte";
|
||||
import ButtonGroup from "components/ButtonGroup.svelte";
|
||||
|
@ -20,6 +21,9 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
export let state: DeckOptionsState;
|
||||
let configList = state.configList;
|
||||
|
||||
let addModalKey: string;
|
||||
let renameModalKey: string;
|
||||
|
||||
function configLabel(entry: ConfigListEntry): string {
|
||||
const count = tr.deckConfigUsedByDecks({ decks: entry.useCount });
|
||||
return `${entry.name} (${count})`;
|
||||
|
@ -28,8 +32,31 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
function blur(event: Event): void {
|
||||
state.setCurrentIndex(parseInt((event.target! as HTMLSelectElement).value));
|
||||
}
|
||||
|
||||
function onAddConfig(text: string): void {
|
||||
const trimmed = text.trim();
|
||||
if (trimmed.length) {
|
||||
state.addConfig(trimmed);
|
||||
}
|
||||
}
|
||||
|
||||
function onRenameConfig(text: string): void {
|
||||
state.setCurrentName(text);
|
||||
}
|
||||
</script>
|
||||
|
||||
<TextInputModal
|
||||
title="Add Config"
|
||||
prompt="Name"
|
||||
onOk={onAddConfig}
|
||||
bind:modalKey={addModalKey} />
|
||||
<TextInputModal
|
||||
title="Rename Config"
|
||||
prompt="Name"
|
||||
onOk={onRenameConfig}
|
||||
value={state.getCurrentName()}
|
||||
bind:modalKey={renameModalKey} />
|
||||
|
||||
<StickyBar>
|
||||
<WithTheming style="--toolbar-size: 2.3rem; --toolbar-wrap: nowrap">
|
||||
<ButtonToolbar class="justify-content-between">
|
||||
|
@ -50,7 +77,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
</ButtonToolbarItem>
|
||||
|
||||
<ButtonToolbarItem>
|
||||
<SaveButton {state} />
|
||||
<SaveButton {state} {addModalKey} {renameModalKey} />
|
||||
</ButtonToolbarItem>
|
||||
</ButtonToolbar>
|
||||
</WithTheming>
|
||||
|
|
|
@ -41,21 +41,5 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
onDestroy(() => registerCleanup?.());
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.editor {
|
||||
// without this, the initial viewport can be wrong
|
||||
overflow-x: hidden;
|
||||
}
|
||||
</style>
|
||||
|
||||
<ConfigSelector {state} />
|
||||
|
||||
<div>
|
||||
<div id="modal">
|
||||
<!-- filled in later-->
|
||||
</div>
|
||||
|
||||
<div class="editor">
|
||||
<ConfigEditor {state} />
|
||||
</div>
|
||||
</div>
|
||||
<ConfigEditor {state} />
|
||||
|
|
|
@ -4,8 +4,10 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
-->
|
||||
<script lang="ts">
|
||||
import * as tr from "lib/i18n";
|
||||
import { textInputModal } from "./textInputModal";
|
||||
import { modalsKey } from "components/contextKeys";
|
||||
import { getContext } from "svelte";
|
||||
import type { DeckOptionsState } from "./lib";
|
||||
import type Modal from "bootstrap/js/dist/modal";
|
||||
|
||||
import ButtonGroup from "components/ButtonGroup.svelte";
|
||||
import ButtonGroupItem from "components/ButtonGroupItem.svelte";
|
||||
|
@ -18,29 +20,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
|
||||
export let state: DeckOptionsState;
|
||||
|
||||
function addConfig(): void {
|
||||
textInputModal({
|
||||
title: "Add Config",
|
||||
prompt: "Name:",
|
||||
onOk: (text: string) => {
|
||||
const trimmed = text.trim();
|
||||
if (trimmed.length) {
|
||||
state.addConfig(trimmed);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function renameConfig(): void {
|
||||
textInputModal({
|
||||
title: "Rename Config",
|
||||
prompt: "Name:",
|
||||
startingValue: state.getCurrentName(),
|
||||
onOk: (text: string) => {
|
||||
state.setCurrentName(text);
|
||||
},
|
||||
});
|
||||
}
|
||||
const modals = getContext<Map<string, Modal>>(modalsKey);
|
||||
|
||||
function removeConfig(): void {
|
||||
// show pop-up after dropdown has gone away
|
||||
|
@ -67,6 +47,17 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
function save(applyToChildDecks: boolean): void {
|
||||
state.save(applyToChildDecks);
|
||||
}
|
||||
|
||||
export let addModalKey: string;
|
||||
export let renameModalKey: string;
|
||||
|
||||
function showAddModal() {
|
||||
modals.get(addModalKey)!.show();
|
||||
}
|
||||
|
||||
function showRenameModal() {
|
||||
modals.get(renameModalKey)!.show();
|
||||
}
|
||||
</script>
|
||||
|
||||
<ButtonGroup>
|
||||
|
@ -78,8 +69,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
<WithDropdownMenu let:createDropdown let:activateDropdown let:menuId>
|
||||
<LabelButton on:mount={createDropdown} on:click={activateDropdown} />
|
||||
<DropdownMenu id={menuId}>
|
||||
<DropdownItem on:click={addConfig}>Add Config</DropdownItem>
|
||||
<DropdownItem on:click={renameConfig}>Rename Config</DropdownItem>
|
||||
<DropdownItem on:click={showAddModal}>Add Config</DropdownItem>
|
||||
<DropdownItem on:click={showRenameModal}>Rename Config</DropdownItem>
|
||||
<DropdownItem on:click={removeConfig}>Remove Config</DropdownItem>
|
||||
<DropdownDivider />
|
||||
<DropdownItem on:click={() => save(true)}>
|
||||
|
|
|
@ -7,43 +7,40 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
@typescript-eslint/no-non-null-assertion: "off",
|
||||
*/
|
||||
import { onMount, onDestroy, getContext } from "svelte";
|
||||
import { nightModeKey } from "components/contextKeys";
|
||||
import { nightModeKey, modalsKey } from "components/contextKeys";
|
||||
import Modal from "bootstrap/js/dist/modal";
|
||||
|
||||
export let title: string;
|
||||
export let prompt: string;
|
||||
export let startingValue = "";
|
||||
export let value = "";
|
||||
export let onOk: (text: string) => void;
|
||||
|
||||
let inputRef: HTMLInputElement;
|
||||
export const modalKey: string = Math.random().toString(36).substring(2);
|
||||
|
||||
const modals = getContext<Map<string, Modal>>(modalsKey);
|
||||
|
||||
let modalRef: HTMLDivElement;
|
||||
let modal: Modal;
|
||||
|
||||
function onShown(): void {
|
||||
inputRef.focus();
|
||||
}
|
||||
|
||||
function onHidden(): void {
|
||||
const container = document.getElementById("modal")!;
|
||||
container.removeChild(container.firstElementChild!);
|
||||
}
|
||||
let inputRef: HTMLInputElement;
|
||||
|
||||
function onOkClicked(): void {
|
||||
onOk(inputRef.value);
|
||||
modal.hide();
|
||||
}
|
||||
|
||||
function onShown(): void {
|
||||
inputRef.focus();
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
const container = document.getElementById("modal")!;
|
||||
container.addEventListener("shown.bs.modal", onShown);
|
||||
container.addEventListener("hidden.bs.modal", onHidden);
|
||||
modal = new Modal(container.firstElementChild!, {});
|
||||
modal.show();
|
||||
modalRef.addEventListener("shown.bs.modal", onShown);
|
||||
modal = new Modal(modalRef);
|
||||
modals.set(modalKey, modal);
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
const container = document.getElementById("modal")!;
|
||||
container.removeEventListener("shown.bs.modal", onShown);
|
||||
container.removeEventListener("hidden.bs.modal", onHidden);
|
||||
modalRef.removeEventListener("shown.bs.modal", onShown);
|
||||
});
|
||||
|
||||
const nightMode = getContext<boolean>(nightModeKey);
|
||||
|
@ -60,7 +57,12 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
}
|
||||
</style>
|
||||
|
||||
<div class="modal fade" tabindex="-1" aria-labelledby="modalLabel" aria-hidden="true">
|
||||
<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">
|
||||
|
@ -77,13 +79,13 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
<div class="mb-3">
|
||||
<label
|
||||
for="prompt-input"
|
||||
class="col-form-label">{prompt}</label>
|
||||
class="col-form-label">{prompt}:</label>
|
||||
<input
|
||||
id="prompt-input"
|
||||
bind:this={inputRef}
|
||||
type="text"
|
||||
class="form-control"
|
||||
value={startingValue} />
|
||||
bind:value />
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
@ -14,7 +14,7 @@ import SpinBoxFloat from "./SpinBoxFloat.svelte";
|
|||
import EnumSelector from "./EnumSelector.svelte";
|
||||
import CheckBox from "./CheckBox.svelte";
|
||||
|
||||
import { nightModeKey } from "components/contextKeys";
|
||||
import { nightModeKey, modalsKey } from "components/contextKeys";
|
||||
|
||||
export async function deckOptions(
|
||||
target: HTMLDivElement,
|
||||
|
@ -35,6 +35,9 @@ export async function deckOptions(
|
|||
const context = new Map();
|
||||
context.set(nightModeKey, nightMode);
|
||||
|
||||
const modals = new Map();
|
||||
context.set(modalsKey, modals);
|
||||
|
||||
const state = new DeckOptionsState(deckId, info);
|
||||
return new DeckOptionsPage({
|
||||
target,
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
// Copyright: Ankitects Pty Ltd and contributors
|
||||
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
|
||||
/* eslint
|
||||
@typescript-eslint/no-non-null-assertion: "off",
|
||||
*/
|
||||
|
||||
import TextInputModal from "./TextInputModal.svelte";
|
||||
|
||||
import { checkNightMode } from "lib/nightmode";
|
||||
import { nightModeKey } from "components/contextKeys";
|
||||
|
||||
export interface TextInputModalProps {
|
||||
title: string;
|
||||
prompt: string;
|
||||
startingValue?: string;
|
||||
onOk: (text: string) => void;
|
||||
}
|
||||
|
||||
export function textInputModal(props: TextInputModalProps): TextInputModal {
|
||||
const target = document.getElementById("modal")!;
|
||||
|
||||
const nightMode = checkNightMode();
|
||||
const context = new Map();
|
||||
context.set(nightModeKey, nightMode);
|
||||
|
||||
return new TextInputModal({
|
||||
target,
|
||||
props,
|
||||
context,
|
||||
} as any);
|
||||
}
|
Loading…
Reference in a new issue