mirror of
https://github.com/ankitects/anki.git
synced 2025-09-18 22:12:21 -04:00
Activate FormatBlockButtons
This commit is contained in:
parent
baff3df381
commit
bd31a19852
8 changed files with 162 additions and 115 deletions
|
@ -3,17 +3,18 @@ Copyright: Ankitects Pty Ltd and contributors
|
||||||
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
-->
|
-->
|
||||||
<script lang="typescript">
|
<script lang="typescript">
|
||||||
import type { ToolbarItem } from "./types";
|
import { setContext } from "svelte";
|
||||||
|
import { dropdownKey } from "./contextKeys";
|
||||||
|
|
||||||
import ButtonGroup from "./ButtonGroup.svelte";
|
import ButtonGroup from "./ButtonGroup.svelte";
|
||||||
|
|
||||||
export let id: string;
|
export let id: string | undefined;
|
||||||
export let className = "";
|
let className = "";
|
||||||
|
export { className as class };
|
||||||
|
|
||||||
function extendClassName(className: string): string {
|
export let api = {};
|
||||||
return `dropdown-menu btn-dropdown-menu ${className}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
export let items: ToolbarItem[];
|
setContext(dropdownKey, null);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
@ -28,4 +29,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<ButtonGroup {id} className={extendClassName(className)} {items} />
|
<ButtonGroup {id} class={`dropdown-menu btn-dropdown-menu ${className}`} {api}>
|
||||||
|
<slot />
|
||||||
|
</ButtonGroup>
|
||||||
|
|
|
@ -7,12 +7,10 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
import { nightModeKey } from "./contextKeys";
|
import { nightModeKey } from "./contextKeys";
|
||||||
|
|
||||||
export let id: string;
|
export let id: string;
|
||||||
export let className = "";
|
let className = "";
|
||||||
export let tooltip: string;
|
export { className as class };
|
||||||
export let label: string;
|
|
||||||
export let shortcutLabel: string | undefined;
|
|
||||||
|
|
||||||
export let onClick: (event: MouseEvent) => void;
|
export let tooltip: string;
|
||||||
|
|
||||||
let buttonRef: HTMLButtonElement;
|
let buttonRef: HTMLButtonElement;
|
||||||
|
|
||||||
|
@ -28,6 +26,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
button {
|
button {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
|
||||||
|
font-size: calc(var(--toolbar-size) / 2.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-day {
|
.btn-day {
|
||||||
|
@ -52,15 +52,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
span {
|
|
||||||
font-size: calc(var(--toolbar-size) / 2.3);
|
|
||||||
color: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
.monospace {
|
|
||||||
font-family: monospace;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
|
@ -70,8 +61,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
class:btn-day={!nightMode}
|
class:btn-day={!nightMode}
|
||||||
class:btn-night={nightMode}
|
class:btn-night={nightMode}
|
||||||
title={tooltip}
|
title={tooltip}
|
||||||
on:click={onClick}
|
on:click
|
||||||
on:mousedown|preventDefault>
|
on:mousedown|preventDefault>
|
||||||
<span class:me-3={shortcutLabel}>{label}</span>
|
<slot />
|
||||||
{#if shortcutLabel}<span class="monospace">{shortcutLabel}</span>{/if}
|
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -3,23 +3,21 @@ Copyright: Ankitects Pty Ltd and contributors
|
||||||
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
-->
|
-->
|
||||||
<script lang="typescript">
|
<script lang="typescript">
|
||||||
import type { ToolbarItem } from "./types";
|
import { setContext } from "svelte";
|
||||||
|
import { dropdownKey } from "./contextKeys";
|
||||||
|
|
||||||
export let id: string;
|
export let id: string | undefined;
|
||||||
export let items: ToolbarItem[];
|
|
||||||
|
setContext(dropdownKey, null);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
ul {
|
div {
|
||||||
background-color: var(--frame-bg);
|
background-color: var(--frame-bg);
|
||||||
border-color: var(--medium-border);
|
border-color: var(--medium-border);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<ul {id} class="dropdown-menu">
|
<div {id}>
|
||||||
{#each items as menuItem}
|
<slot />
|
||||||
<li>
|
</div>
|
||||||
<svelte:component this={menuItem.component} {...menuItem} />
|
|
||||||
</li>
|
|
||||||
{/each}
|
|
||||||
</ul>
|
|
||||||
|
|
|
@ -4,8 +4,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
-->
|
-->
|
||||||
<script lang="typescript">
|
<script lang="typescript">
|
||||||
import type { Readable } from "svelte/store";
|
import type { Readable } from "svelte/store";
|
||||||
import { getContext, onMount, createEventDispatcher } from "svelte";
|
import { hasContext, getContext, onMount, createEventDispatcher } from "svelte";
|
||||||
import { disabledKey, nightModeKey } from "./contextKeys";
|
import { disabledKey, nightModeKey, dropdownKey } from "./contextKeys";
|
||||||
|
|
||||||
export let id: string;
|
export let id: string;
|
||||||
let className = "";
|
let className = "";
|
||||||
|
@ -14,14 +14,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
export let tooltip: string | undefined;
|
export let tooltip: string | undefined;
|
||||||
export let active = false;
|
export let active = false;
|
||||||
export let disables = true;
|
export let disables = true;
|
||||||
export let dropdownToggle = false;
|
|
||||||
|
|
||||||
$: extraProps = dropdownToggle
|
|
||||||
? {
|
|
||||||
"data-bs-toggle": "dropdown",
|
|
||||||
"aria-expanded": "false",
|
|
||||||
}
|
|
||||||
: {};
|
|
||||||
|
|
||||||
let buttonRef: HTMLButtonElement;
|
let buttonRef: HTMLButtonElement;
|
||||||
|
|
||||||
|
@ -30,6 +22,9 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
const nightMode = getContext<boolean>(nightModeKey);
|
const nightMode = getContext<boolean>(nightModeKey);
|
||||||
|
|
||||||
|
const dropdown = getContext(dropdownKey);
|
||||||
|
const dropdownProps = dropdown?.getDropdownTriggerProps() ?? {};
|
||||||
|
|
||||||
const dispatch = createEventDispatcher();
|
const dispatch = createEventDispatcher();
|
||||||
onMount(() => dispatch("mount", { button: buttonRef }));
|
onMount(() => dispatch("mount", { button: buttonRef }));
|
||||||
</script>
|
</script>
|
||||||
|
@ -71,13 +66,13 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
{id}
|
{id}
|
||||||
class={`btn ${className}`}
|
class={`btn ${className}`}
|
||||||
class:active
|
class:active
|
||||||
class:dropdown-toggle={dropdownToggle}
|
|
||||||
class:btn-day={!nightMode}
|
class:btn-day={!nightMode}
|
||||||
class:btn-night={nightMode}
|
class:btn-night={nightMode}
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
title={tooltip}
|
title={tooltip}
|
||||||
disabled={_disabled}
|
disabled={_disabled}
|
||||||
{...extraProps}
|
class:dropdown-toggle={Boolean(dropdown)}
|
||||||
|
{...dropdownProps}
|
||||||
on:click
|
on:click
|
||||||
on:mousedown|preventDefault>
|
on:mousedown|preventDefault>
|
||||||
<span class="p-1"><slot /></span>
|
<span class="p-1"><slot /></span>
|
||||||
|
|
|
@ -3,39 +3,33 @@ Copyright: Ankitects Pty Ltd and contributors
|
||||||
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
-->
|
-->
|
||||||
<script lang="typescript">
|
<script lang="typescript">
|
||||||
import type { DynamicSvelteComponent } from "sveltelib/dynamicComponent";
|
|
||||||
import type { ToolbarItem } from "./types";
|
|
||||||
|
|
||||||
import Dropdown from "bootstrap/js/dist/dropdown";
|
import Dropdown from "bootstrap/js/dist/dropdown";
|
||||||
|
|
||||||
/* Bootstrap dropdown are normally declared alongside the associated button
|
import { setContext } from "svelte";
|
||||||
* However we cannot do that, as the menus cannot be declared in sticky-positioned elements
|
import { dropdownKey } from "./contextKeys";
|
||||||
*/
|
|
||||||
export let button: ToolbarItem;
|
|
||||||
export let menuId: string;
|
|
||||||
|
|
||||||
function extend({
|
setContext(dropdownKey, {
|
||||||
className,
|
getDropdownTriggerProps: () => ({
|
||||||
...rest
|
"data-bs-toggle": "dropdown",
|
||||||
}: DynamicSvelteComponent): DynamicSvelteComponent {
|
"aria-expanded": "false",
|
||||||
return {
|
}),
|
||||||
dropdownToggle: true,
|
});
|
||||||
...rest,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function createDropdown({ detail }: CustomEvent): void {
|
const menuId = Math.random().toString(36).substring(2);
|
||||||
const button: HTMLButtonElement = detail.button;
|
|
||||||
|
|
||||||
|
/* Normally dropdown and trigger are associated with a
|
||||||
|
/* common ancestor with .dropdown class */
|
||||||
|
function createDropdown(button: HTMLElement): void {
|
||||||
/* Prevent focus on menu activation */
|
/* Prevent focus on menu activation */
|
||||||
const noop = () => {};
|
const noop = () => {};
|
||||||
Object.defineProperty(button, "focus", { value: noop });
|
Object.defineProperty(button, "focus", { value: noop });
|
||||||
|
|
||||||
/* Set custom menu without using .dropdown
|
/* Set custom menu without using .dropdown
|
||||||
* Rendering the menu here would cause the menu to
|
* Rendering the menu here would cause the menu to
|
||||||
* be displayed outside of the visible area
|
* be displayed outside of the visible area */
|
||||||
*/
|
|
||||||
const dropdown = new Dropdown(button);
|
const dropdown = new Dropdown(button);
|
||||||
|
|
||||||
const menu = (button.getRootNode() as Document) /* or shadow root */
|
const menu = (button.getRootNode() as Document) /* or shadow root */
|
||||||
.getElementById(menuId);
|
.getElementById(menuId);
|
||||||
|
|
||||||
|
@ -47,7 +41,4 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:component
|
<slot {createDropdown} {menuId} />
|
||||||
this={button.component}
|
|
||||||
{...extend(button)}
|
|
||||||
on:mount={createDropdown} />
|
|
||||||
|
|
|
@ -4,3 +4,4 @@ export const nightModeKey = Symbol("nightMode");
|
||||||
export const disabledKey = Symbol("disabled");
|
export const disabledKey = Symbol("disabled");
|
||||||
|
|
||||||
export const buttonGroupKey = Symbol("buttonGroup");
|
export const buttonGroupKey = Symbol("buttonGroup");
|
||||||
|
export const dropdownKey = Symbol("dropdown");
|
||||||
|
|
|
@ -57,5 +57,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
<StickyBar>
|
<StickyBar>
|
||||||
<NoteTypeButtons />
|
<NoteTypeButtons />
|
||||||
<FormatInlineButtons />
|
<FormatInlineButtons />
|
||||||
|
<FormatBlockButtons />
|
||||||
</StickyBar>
|
</StickyBar>
|
||||||
</WithTheming>
|
</WithTheming>
|
||||||
|
|
|
@ -42,52 +42,120 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<ButtonDropdown id="listFormatting">
|
|
||||||
<ButtonGroup id="justify" {api}>
|
|
||||||
<IconButton command="justifyLeft" tooltip={tr.editingAlignLeft()}>
|
|
||||||
{@html justifyLeftIcon}
|
|
||||||
</IconButton>
|
|
||||||
|
|
||||||
<IconButton command="justifyCenter" tooltip={tr.editingCenter()}>
|
|
||||||
{@html justifyCenterIcon}
|
|
||||||
</IconButton>
|
|
||||||
|
|
||||||
<IconButton command="justifyCenter" tooltip={tr.editingCenter()}>
|
|
||||||
{@html justifyCenterIcon}
|
|
||||||
</IconButton>
|
|
||||||
|
|
||||||
<IconButton command="justifyRight" tooltip={tr.editingAlignRight()}>
|
|
||||||
{@html justifyRightIcon}
|
|
||||||
</IconButton>
|
|
||||||
|
|
||||||
<IconButton command="justifyFull" tooltip={tr.editingJustify()}>
|
|
||||||
{@html justifyFullIcon}
|
|
||||||
</IconButton>
|
|
||||||
</ButtonGroup>
|
|
||||||
|
|
||||||
<ButtonGroup id="indentation" {api}>
|
|
||||||
<IconButton on:click={outdentListItem} tooltip={tr.editingOutdent}>
|
|
||||||
{@html outdentIcon}
|
|
||||||
</IconButton>
|
|
||||||
|
|
||||||
<IconButton on:click={indentListItem} tooltip={tr.editingIndent}>
|
|
||||||
{@html indentIcon}
|
|
||||||
</IconButton>
|
|
||||||
</ButtonGroup>
|
|
||||||
</ButtonDropdown>
|
|
||||||
|
|
||||||
<ButtonGroup id="blockFormatting" {api}>
|
<ButtonGroup id="blockFormatting" {api}>
|
||||||
<IconButton command="insertUnorderedList" tooltip={tr.editingUnorderedList()}>
|
<WithState
|
||||||
{@html ulIcon}
|
key="insertUnorderedList"
|
||||||
</IconButton>
|
update={() => document.queryCommandState('insertUnorderedList')}
|
||||||
|
let:state={active}
|
||||||
|
let:updateState>
|
||||||
|
<IconButton
|
||||||
|
tooltip={tr.editingUnorderedList()}
|
||||||
|
{active}
|
||||||
|
on:click={(event) => {
|
||||||
|
document.execCommand('insertUnorderedList');
|
||||||
|
updateState(event);
|
||||||
|
}}>
|
||||||
|
{@html ulIcon}
|
||||||
|
</IconButton>
|
||||||
|
</WithState>
|
||||||
|
|
||||||
<IconButton command="insertOrderedList" tooltip={tr.editingOrderedList()}>
|
<WithState
|
||||||
{@html olIcon}
|
key="insertOrderedList"
|
||||||
</IconButton>
|
update={() => document.queryCommandState('insertOrderedList')}
|
||||||
|
let:state={active}
|
||||||
|
let:updateState>
|
||||||
|
<IconButton
|
||||||
|
tooltip={tr.editingOrderedList()}
|
||||||
|
{active}
|
||||||
|
on:click={(event) => {
|
||||||
|
document.execCommand('insertOrderedList');
|
||||||
|
updateState(event);
|
||||||
|
}}>
|
||||||
|
{@html olIcon}
|
||||||
|
</IconButton>
|
||||||
|
</WithState>
|
||||||
|
|
||||||
<WithDropdownMenu menuId="listFormatting">
|
<WithDropdownMenu let:createDropdown let:menuId>
|
||||||
<IconButton>
|
<IconButton on:mount={(event) => createDropdown(event.detail.button)}>
|
||||||
{@html listOptionsIcon}
|
{@html listOptionsIcon}
|
||||||
</IconButton>
|
</IconButton>
|
||||||
|
|
||||||
|
<ButtonDropdown id={menuId}>
|
||||||
|
<ButtonGroup id="justify" {api}>
|
||||||
|
<WithState
|
||||||
|
key="justifyLeft"
|
||||||
|
update={() => document.queryCommandState('justifyLeft')}
|
||||||
|
let:state={active}
|
||||||
|
let:updateState>
|
||||||
|
<IconButton
|
||||||
|
tooltip={tr.editingAlignLeft()}
|
||||||
|
{active}
|
||||||
|
on:click={(event) => {
|
||||||
|
document.execCommand('justifyLeft');
|
||||||
|
updateState(event);
|
||||||
|
}}>
|
||||||
|
{@html justifyLeftIcon}
|
||||||
|
</IconButton>
|
||||||
|
</WithState>
|
||||||
|
|
||||||
|
<WithState
|
||||||
|
key="justifyCenter"
|
||||||
|
update={() => document.queryCommandState('justifyCenter')}
|
||||||
|
let:state={active}
|
||||||
|
let:updateState>
|
||||||
|
<IconButton
|
||||||
|
tooltip={tr.editingCenter()}
|
||||||
|
{active}
|
||||||
|
on:click={(event) => {
|
||||||
|
document.execCommand('justifyCenter');
|
||||||
|
updateState(event);
|
||||||
|
}}>
|
||||||
|
{@html justifyCenterIcon}
|
||||||
|
</IconButton>
|
||||||
|
</WithState>
|
||||||
|
|
||||||
|
<WithState
|
||||||
|
key="justifyRight"
|
||||||
|
update={() => document.queryCommandState('justifyRight')}
|
||||||
|
let:state={active}
|
||||||
|
let:updateState>
|
||||||
|
<IconButton
|
||||||
|
tooltip={tr.editingAlignRight()}
|
||||||
|
{active}
|
||||||
|
on:click={(event) => {
|
||||||
|
document.execCommand('justifyRight');
|
||||||
|
updateState(event);
|
||||||
|
}}>
|
||||||
|
{@html justifyRightIcon}
|
||||||
|
</IconButton>
|
||||||
|
</WithState>
|
||||||
|
|
||||||
|
<WithState
|
||||||
|
key="justifyFull"
|
||||||
|
update={() => document.queryCommandState('justifyFull')}
|
||||||
|
let:state={active}
|
||||||
|
let:updateState>
|
||||||
|
<IconButton
|
||||||
|
tooltip={tr.editingJustify()}
|
||||||
|
{active}
|
||||||
|
on:click={(event) => {
|
||||||
|
document.execCommand('justifyFull');
|
||||||
|
updateState(event);
|
||||||
|
}}>
|
||||||
|
{@html justifyFullIcon}
|
||||||
|
</IconButton>
|
||||||
|
</WithState>
|
||||||
|
</ButtonGroup>
|
||||||
|
|
||||||
|
<ButtonGroup id="indentation" {api}>
|
||||||
|
<IconButton on:click={outdentListItem} tooltip={tr.editingOutdent}>
|
||||||
|
{@html outdentIcon}
|
||||||
|
</IconButton>
|
||||||
|
|
||||||
|
<IconButton on:click={indentListItem} tooltip={tr.editingIndent}>
|
||||||
|
{@html indentIcon}
|
||||||
|
</IconButton>
|
||||||
|
</ButtonGroup>
|
||||||
|
</ButtonDropdown>
|
||||||
</WithDropdownMenu>
|
</WithDropdownMenu>
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
|
|
Loading…
Reference in a new issue