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:
parent
4d51ee8a64
commit
6d8fb35fab
@ -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 }):
|
||||||
|
@ -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 */
|
||||||
|
@ -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>
|
||||||
|
@ -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>
|
|
@ -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";
|
|
||||||
|
@ -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");
|
||||||
|
Loading…
Reference in New Issue
Block a user