Make field description a placeholder inside EditingArea (#1912)

* Move field description into EditingArea as placeholder

* Prevent insertion of breaks into empty fields

to allow :empty CSS selector to also work on fields other than the first one.

* Remove redundant setContext from EditingArea

* Fix import order

* Revert "Prevent insertion of breaks into empty fields"

This reverts commit 1615fd5cf441b1047dae6a34265acb9c5cae50b2.

* Use class:empty instead of :empty CSS pseudo-class

* Restrict description to single line, ellipse overflow

* Make description in field dialog a bit clearer
This commit is contained in:
Matthias Metelka 2022-06-17 03:02:30 +02:00 committed by GitHub
parent 4d51ee8a64
commit 6d8fb35fab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 33 additions and 48 deletions

View File

@ -4,7 +4,7 @@ fields-editing-font = Editing Font
fields-field = Field: fields-field = Field:
fields-field-name = Field name: fields-field-name = Field name:
fields-description = Description 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-fields-for = Fields for { $val }
fields-font = Font: fields-font = Font:
fields-new-position-1 = New position (1...{ $val }): fields-new-position-1 = New position (1...{ $val }):

View File

@ -7,9 +7,11 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
</script> </script>
<script lang="ts"> <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 { updateAllState } from "../components/WithState.svelte";
import { descriptionKey } from "../lib/context-keys";
import actionList from "../sveltelib/action-list"; import actionList from "../sveltelib/action-list";
import type { MirrorAction } from "../sveltelib/dom-mirror"; import type { MirrorAction } from "../sveltelib/dom-mirror";
import type { SetupInputHandlerAction } from "../sveltelib/input-handler"; import type { SetupInputHandlerAction } from "../sveltelib/input-handler";
@ -33,10 +35,18 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
const [focusHandler, setupFocusHandling] = useFocusHandler(); const [focusHandler, setupFocusHandling] = useFocusHandler();
Object.assign(api, { focusHandler }); Object.assign(api, { focusHandler });
const description = getContext<Readable<string>>(descriptionKey);
$: descriptionCSSValue = `"${$description}"`;
let innerHTML = "";
$: empty = ["", "<br>"].includes(innerHTML);
</script> </script>
<anki-editable <anki-editable
class:empty
contenteditable="true" contenteditable="true"
bind:innerHTML
use:resolve use:resolve
use:setupFocusHandling use:setupFocusHandling
use:preventBuiltinShortcuts use:preventBuiltinShortcuts
@ -46,11 +56,13 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
on:blur on:blur
on:click={updateAllState} on:click={updateAllState}
on:keyup={updateAllState} on:keyup={updateAllState}
style="--description: {descriptionCSSValue}"
/> />
<style lang="scss"> <style lang="scss">
anki-editable { anki-editable {
display: block; display: block;
position: relative;
padding: 6px; padding: 6px;
overflow: auto; overflow: auto;
overflow-wrap: anywhere; overflow-wrap: anywhere;
@ -60,6 +72,17 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
&:focus { &:focus {
outline: none; 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 */ /* editable-base.scss contains styling targeting user HTML */

View File

@ -10,10 +10,10 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
export interface FieldData { export interface FieldData {
name: string; name: string;
description: string;
fontFamily: string; fontFamily: string;
fontSize: number; fontSize: number;
direction: "ltr" | "rtl"; direction: "ltr" | "rtl";
description: string;
} }
export interface EditorFieldAPI { export interface EditorFieldAPI {
@ -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 type { Writable } from "svelte/store";
import { 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 { promiseWithResolver } from "../lib/promise";
import type { Destroyable } from "./destroyable"; import type { Destroyable } from "./destroyable";
import EditingArea from "./EditingArea.svelte"; import EditingArea from "./EditingArea.svelte";
import FieldState from "./FieldState.svelte"; import FieldState from "./FieldState.svelte";
import LabelContainer from "./LabelContainer.svelte"; import LabelContainer from "./LabelContainer.svelte";
import LabelDescription from "./LabelDescription.svelte";
import LabelName from "./LabelName.svelte"; import LabelName from "./LabelName.svelte";
export let content: Writable<string>; export let content: Writable<string>;
@ -50,6 +49,11 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
$: $directionStore = field.direction; $: $directionStore = field.direction;
const descriptionStore = writable<string>();
setContext(descriptionKey, descriptionStore);
$: $descriptionStore = field.description;
const editingArea: Partial<EditingAreaAPI> = {}; const editingArea: Partial<EditingAreaAPI> = {};
const [element, elementResolve] = promiseWithResolver<HTMLElement>(); const [element, elementResolve] = promiseWithResolver<HTMLElement>();
@ -79,9 +83,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
<LabelName> <LabelName>
{field.name} {field.name}
</LabelName> </LabelName>
{#if field.description}
<LabelDescription description={field.description} />
{/if}
</span> </span>
<FieldState><slot name="field-state" /></FieldState> <FieldState><slot name="field-state" /></FieldState>
</LabelContainer> </LabelContainer>

View File

@ -1,39 +0,0 @@
<!--
Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
<script lang="ts">
import Badge from "../components/Badge.svelte";
import WithTooltip from "../components/WithTooltip.svelte";
import { descriptionIcon } from "./icons";
export let description: string;
</script>
<span>
<WithTooltip
tooltip={description}
showDelay={250}
offset={[0, 20]}
placement="right"
let:createTooltip
>
<Badge
--icon-align="sub"
iconSize={65}
on:mount={(event) => createTooltip(event.detail.span)}
>
{@html descriptionIcon}
</Badge>
</WithTooltip>
</span>
<style lang="scss">
span {
opacity: 0.4;
&:hover {
opacity: 0.8;
}
}
</style>

View File

@ -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 stickyOff } from "@mdi/svg/svg/pin-off-outline.svg";
export { default as stickyOn } from "@mdi/svg/svg/pin-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 htmlOff } from "@mdi/svg/svg/xml.svg";
export { default as descriptionIcon } from "bootstrap-icons/icons/info-circle.svg";

View File

@ -4,3 +4,4 @@
export const fontFamilyKey = Symbol("fontFamily"); export const fontFamilyKey = Symbol("fontFamily");
export const fontSizeKey = Symbol("fontSize"); export const fontSizeKey = Symbol("fontSize");
export const directionKey = Symbol("direction"); export const directionKey = Symbol("direction");
export const descriptionKey = Symbol("description");