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 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>

View file

@ -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>

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 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>

View file

@ -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>

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 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} />

View file

@ -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");

View file

@ -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>

View file

@ -42,27 +42,109 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
} }
</script> </script>
<ButtonDropdown id="listFormatting"> <ButtonGroup id="blockFormatting" {api}>
<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>
<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 let:createDropdown let:menuId>
<IconButton on:mount={(event) => createDropdown(event.detail.button)}>
{@html listOptionsIcon}
</IconButton>
<ButtonDropdown id={menuId}>
<ButtonGroup id="justify" {api}> <ButtonGroup id="justify" {api}>
<IconButton command="justifyLeft" tooltip={tr.editingAlignLeft()}> <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} {@html justifyLeftIcon}
</IconButton> </IconButton>
</WithState>
<IconButton command="justifyCenter" tooltip={tr.editingCenter()}> <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} {@html justifyCenterIcon}
</IconButton> </IconButton>
</WithState>
<IconButton command="justifyCenter" tooltip={tr.editingCenter()}> <WithState
{@html justifyCenterIcon} key="justifyRight"
</IconButton> update={() => document.queryCommandState('justifyRight')}
let:state={active}
<IconButton command="justifyRight" tooltip={tr.editingAlignRight()}> let:updateState>
<IconButton
tooltip={tr.editingAlignRight()}
{active}
on:click={(event) => {
document.execCommand('justifyRight');
updateState(event);
}}>
{@html justifyRightIcon} {@html justifyRightIcon}
</IconButton> </IconButton>
</WithState>
<IconButton command="justifyFull" tooltip={tr.editingJustify()}> <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} {@html justifyFullIcon}
</IconButton> </IconButton>
</WithState>
</ButtonGroup> </ButtonGroup>
<ButtonGroup id="indentation" {api}> <ButtonGroup id="indentation" {api}>
@ -74,20 +156,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
{@html indentIcon} {@html indentIcon}
</IconButton> </IconButton>
</ButtonGroup> </ButtonGroup>
</ButtonDropdown> </ButtonDropdown>
<ButtonGroup id="blockFormatting" {api}>
<IconButton command="insertUnorderedList" tooltip={tr.editingUnorderedList()}>
{@html ulIcon}
</IconButton>
<IconButton command="insertOrderedList" tooltip={tr.editingOrderedList()}>
{@html olIcon}
</IconButton>
<WithDropdownMenu menuId="listFormatting">
<IconButton>
{@html listOptionsIcon}
</IconButton>
</WithDropdownMenu> </WithDropdownMenu>
</ButtonGroup> </ButtonGroup>