Activate FormatBlockButtons

This commit is contained in:
Henrik Giesel 2021-04-28 22:15:24 +02:00
parent baff3df381
commit bd31a19852
8 changed files with 162 additions and 115 deletions

View file

@ -3,17 +3,18 @@ Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
<script lang="typescript">
import type { ToolbarItem } from "./types";
import { setContext } from "svelte";
import { dropdownKey } from "./contextKeys";
import ButtonGroup from "./ButtonGroup.svelte";
export let id: string;
export let className = "";
export let id: string | undefined;
let className = "";
export { className as class };
function extendClassName(className: string): string {
return `dropdown-menu btn-dropdown-menu ${className}`;
}
export let api = {};
export let items: ToolbarItem[];
setContext(dropdownKey, null);
</script>
<style>
@ -28,4 +29,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
}
</style>
<ButtonGroup {id} className={extendClassName(className)} {items} />
<ButtonGroup {id} class={`dropdown-menu btn-dropdown-menu ${className}`} {api}>
<slot />
</ButtonGroup>

View file

@ -7,12 +7,10 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import { nightModeKey } from "./contextKeys";
export let id: string;
export let className = "";
export let tooltip: string;
export let label: string;
export let shortcutLabel: string | undefined;
let className = "";
export { className as class };
export let onClick: (event: MouseEvent) => void;
export let tooltip: string;
let buttonRef: HTMLButtonElement;
@ -28,6 +26,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
button {
display: flex;
justify-content: space-between;
font-size: calc(var(--toolbar-size) / 2.3);
}
.btn-day {
@ -52,15 +52,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
color: white;
}
}
span {
font-size: calc(var(--toolbar-size) / 2.3);
color: inherit;
}
.monospace {
font-family: monospace;
}
</style>
<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-night={nightMode}
title={tooltip}
on:click={onClick}
on:click
on:mousedown|preventDefault>
<span class:me-3={shortcutLabel}>{label}</span>
{#if shortcutLabel}<span class="monospace">{shortcutLabel}</span>{/if}
<slot />
</button>

View file

@ -3,23 +3,21 @@ Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
<script lang="typescript">
import type { ToolbarItem } from "./types";
import { setContext } from "svelte";
import { dropdownKey } from "./contextKeys";
export let id: string;
export let items: ToolbarItem[];
export let id: string | undefined;
setContext(dropdownKey, null);
</script>
<style lang="scss">
ul {
div {
background-color: var(--frame-bg);
border-color: var(--medium-border);
}
</style>
<ul {id} class="dropdown-menu">
{#each items as menuItem}
<li>
<svelte:component this={menuItem.component} {...menuItem} />
</li>
{/each}
</ul>
<div {id}>
<slot />
</div>

View file

@ -4,8 +4,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
<script lang="typescript">
import type { Readable } from "svelte/store";
import { getContext, onMount, createEventDispatcher } from "svelte";
import { disabledKey, nightModeKey } from "./contextKeys";
import { hasContext, getContext, onMount, createEventDispatcher } from "svelte";
import { disabledKey, nightModeKey, dropdownKey } from "./contextKeys";
export let id: string;
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 active = false;
export let disables = true;
export let dropdownToggle = false;
$: extraProps = dropdownToggle
? {
"data-bs-toggle": "dropdown",
"aria-expanded": "false",
}
: {};
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 dropdown = getContext(dropdownKey);
const dropdownProps = dropdown?.getDropdownTriggerProps() ?? {};
const dispatch = createEventDispatcher();
onMount(() => dispatch("mount", { button: buttonRef }));
</script>
@ -71,13 +66,13 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
{id}
class={`btn ${className}`}
class:active
class:dropdown-toggle={dropdownToggle}
class:btn-day={!nightMode}
class:btn-night={nightMode}
tabindex="-1"
title={tooltip}
disabled={_disabled}
{...extraProps}
class:dropdown-toggle={Boolean(dropdown)}
{...dropdownProps}
on:click
on:mousedown|preventDefault>
<span class="p-1"><slot /></span>

View file

@ -3,39 +3,33 @@ Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
<script lang="typescript">
import type { DynamicSvelteComponent } from "sveltelib/dynamicComponent";
import type { ToolbarItem } from "./types";
import Dropdown from "bootstrap/js/dist/dropdown";
/* Bootstrap dropdown are normally declared alongside the associated button
* However we cannot do that, as the menus cannot be declared in sticky-positioned elements
*/
export let button: ToolbarItem;
export let menuId: string;
import { setContext } from "svelte";
import { dropdownKey } from "./contextKeys";
function extend({
className,
...rest
}: DynamicSvelteComponent): DynamicSvelteComponent {
return {
dropdownToggle: true,
...rest,
};
}
setContext(dropdownKey, {
getDropdownTriggerProps: () => ({
"data-bs-toggle": "dropdown",
"aria-expanded": "false",
}),
});
function createDropdown({ detail }: CustomEvent): void {
const button: HTMLButtonElement = detail.button;
const menuId = Math.random().toString(36).substring(2);
/* Normally dropdown and trigger are associated with a
/* common ancestor with .dropdown class */
function createDropdown(button: HTMLElement): void {
/* Prevent focus on menu activation */
const noop = () => {};
Object.defineProperty(button, "focus", { value: noop });
/* Set custom menu without using .dropdown
* 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 menu = (button.getRootNode() as Document) /* or shadow root */
.getElementById(menuId);
@ -47,7 +41,4 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
}
</script>
<svelte:component
this={button.component}
{...extend(button)}
on:mount={createDropdown} />
<slot {createDropdown} {menuId} />

View file

@ -4,3 +4,4 @@ export const nightModeKey = Symbol("nightMode");
export const disabledKey = Symbol("disabled");
export const buttonGroupKey = Symbol("buttonGroup");
export const dropdownKey = Symbol("dropdown");

View file

@ -57,5 +57,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
<StickyBar>
<NoteTypeButtons />
<FormatInlineButtons />
<FormatBlockButtons />
</StickyBar>
</WithTheming>

View file

@ -42,52 +42,120 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
}
</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}>
<IconButton command="insertUnorderedList" tooltip={tr.editingUnorderedList()}>
{@html ulIcon}
</IconButton>
<WithState
key="insertUnorderedList"
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()}>
{@html olIcon}
</IconButton>
<WithState
key="insertOrderedList"
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">
<IconButton>
<WithDropdownMenu let:createDropdown let:menuId>
<IconButton on:mount={(event) => createDropdown(event.detail.button)}>
{@html listOptionsIcon}
</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>
</ButtonGroup>