Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make field description a placeholder inside EditingArea #1912

Merged
merged 8 commits into from Jun 17, 2022
2 changes: 1 addition & 1 deletion ftl/core/fields.ftl
Expand Up @@ -4,7 +4,7 @@ fields-editing-font = Editing Font
fields-field = Field:
fields-field-name = Field name:
fields-description = Description
fields-description-placeholder = Tooltip to show next to the field's name in the editing screen
fields-description-placeholder = Text to show inside the field when it's empty
fields-fields-for = Fields for { $val }
fields-font = Font:
fields-new-position-1 = New position (1...{ $val }):
Expand Down
25 changes: 24 additions & 1 deletion ts/editable/ContentEditable.svelte
Expand Up @@ -7,9 +7,11 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
</script>

<script lang="ts">
import type { Writable } from "svelte/store";
import { getContext } from "svelte";
import type { Readable, Writable } from "svelte/store";

import { updateAllState } from "../components/WithState.svelte";
import { descriptionKey } from "../lib/context-keys";
import actionList from "../sveltelib/action-list";
import type { MirrorAction } from "../sveltelib/dom-mirror";
import type { SetupInputHandlerAction } from "../sveltelib/input-handler";
Expand All @@ -33,10 +35,18 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
const [focusHandler, setupFocusHandling] = useFocusHandler();

Object.assign(api, { focusHandler });

const description = getContext<Readable<string>>(descriptionKey);
$: descriptionCSSValue = `"${$description}"`;

let innerHTML = "";
$: empty = ["", "<br>"].includes(innerHTML);
</script>

<anki-editable
class:empty
contenteditable="true"
bind:innerHTML
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm pretty sure, this is a one-way bind: Changes to innerHTML within the ContentEditable component will be pushed to the element, however not the other way around.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I thought the :bind directive would always create two-way bindings. With the CSS :empty pseudo-class this wasn't an issue and I forgot to test this again with the new approach.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there an idiomatic way of doing this? I don't want to pollute the component with a vanilla JS two-way binding. Also, if there's an alternative to appending <br> to contenteditable in order to allow newlines without crashing, I would prefer the CSS (:empty) solution.

use:resolve
use:setupFocusHandling
use:preventBuiltinShortcuts
Expand All @@ -46,11 +56,13 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
on:blur
on:click={updateAllState}
on:keyup={updateAllState}
style="--description: {descriptionCSSValue}"
/>

<style lang="scss">
anki-editable {
display: block;
position: relative;
padding: 6px;
overflow: auto;
overflow-wrap: anywhere;
Expand All @@ -60,6 +72,17 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
&:focus {
outline: none;
}
&.empty::before {
content: var(--description);
opacity: 0.4;
cursor: text;
/* stay on single line */
position: absolute;
max-width: 95%;
overflow-x: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}

/* editable-base.scss contains styling targeting user HTML */
Expand Down
13 changes: 7 additions & 6 deletions ts/editor/EditorField.svelte
Expand Up @@ -10,10 +10,10 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
export interface FieldData {
name: string;
description: string;
fontFamily: string;
fontSize: number;
direction: "ltr" | "rtl";
description: string;
}
export interface EditorFieldAPI {
Expand All @@ -33,13 +33,12 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import type { Writable } from "svelte/store";
import { writable } from "svelte/store";
import { directionKey } from "../lib/context-keys";
import { descriptionKey, directionKey } from "../lib/context-keys";
import { promiseWithResolver } from "../lib/promise";
import type { Destroyable } from "./destroyable";
import EditingArea from "./EditingArea.svelte";
import FieldState from "./FieldState.svelte";
import LabelContainer from "./LabelContainer.svelte";
import LabelDescription from "./LabelDescription.svelte";
import LabelName from "./LabelName.svelte";
export let content: Writable<string>;
Expand All @@ -50,6 +49,11 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
$: $directionStore = field.direction;
const descriptionStore = writable<string>();
setContext(descriptionKey, descriptionStore);
$: $descriptionStore = field.description;
const editingArea: Partial<EditingAreaAPI> = {};
const [element, elementResolve] = promiseWithResolver<HTMLElement>();
Expand Down Expand Up @@ -79,9 +83,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
<LabelName>
{field.name}
</LabelName>
{#if field.description}
<LabelDescription description={field.description} />
{/if}
</span>
<FieldState><slot name="field-state" /></FieldState>
</LabelContainer>
Expand Down
39 changes: 0 additions & 39 deletions ts/editor/LabelDescription.svelte

This file was deleted.

1 change: 0 additions & 1 deletion ts/editor/icons.ts
Expand Up @@ -12,4 +12,3 @@ export { default as richTextOn } from "@mdi/svg/svg/eye-outline.svg";
export { default as stickyOff } from "@mdi/svg/svg/pin-off-outline.svg";
export { default as stickyOn } from "@mdi/svg/svg/pin-outline.svg";
export { default as htmlOff } from "@mdi/svg/svg/xml.svg";
export { default as descriptionIcon } from "bootstrap-icons/icons/info-circle.svg";
1 change: 1 addition & 0 deletions ts/lib/context-keys.ts
Expand Up @@ -4,3 +4,4 @@
export const fontFamilyKey = Symbol("fontFamily");
export const fontSizeKey = Symbol("fontSize");
export const directionKey = Symbol("direction");
export const descriptionKey = Symbol("description");