Use dynamicComponent instead of withLazyProps

This commit is contained in:
Henrik Giesel 2021-04-08 16:29:28 +02:00
parent 28201670ee
commit c7590c8ef1
17 changed files with 170 additions and 100 deletions

View file

@ -40,6 +40,7 @@ ts_library(
exclude = ["index.ts"],
),
deps = [
"//ts/sveltelib",
"//ts/lib",
"//ts/lib:backend_proto",
"//ts:image_module_support",

6
ts/editor-toolbar/ColorPicker.d.ts vendored Normal file
View file

@ -0,0 +1,6 @@
export interface ColorPickerProps {
id?: string;
className?: string;
title: string;
onChange: (event: Event) => void;
}

View file

@ -0,0 +1,8 @@
export interface CommandIconButtonProps {
id?: string;
className?: string;
title: string;
icon: string;
command: string;
activatable?: boolean;
}

View file

@ -41,7 +41,7 @@
export let props: Record<string, string> = {};
export let title: string;
export let icon = "";
export let icon;
export let command: string;
export let activatable = true;

9
ts/editor-toolbar/DropdownItem.d.ts vendored Normal file
View file

@ -0,0 +1,9 @@
export interface DropdownItemProps {
id?: string;
className?: string;
title: string;
onClick: (event: MouseEvent) => void;
label: string;
endLabel: string;
}

6
ts/editor-toolbar/DropdownMenu.d.ts vendored Normal file
View file

@ -0,0 +1,6 @@
import type { DynamicSvelteComponent } from "sveltelib/dynamicComponent";
export interface DropdownMenuProps {
id: string;
menuItems: DynamicSvelteComponent[];
}

7
ts/editor-toolbar/IconButton.d.ts vendored Normal file
View file

@ -0,0 +1,7 @@
export interface IconButtonProps {
id?: string;
className?: string;
title: string;
icon: string;
onClick: (event: MouseEvent) => void;
}

9
ts/editor-toolbar/LabelButton.d.ts vendored Normal file
View file

@ -0,0 +1,9 @@
export interface LabelButtonProps {
id?: string;
className?: string;
label: string;
title: string;
onClick: (event: MouseEvent) => void;
disables?: boolean;
}

View file

@ -0,0 +1,6 @@
import type { ButtonDefinition } from "./types";
export interface WithDropdownMenuProps {
button: ButtonDefinition;
menuId: string;
}

View file

@ -1,7 +1,9 @@
import IconButton from "./IconButton.svelte";
import type { IconButtonProps } from "./IconButton";
import ColorPicker from "./ColorPicker.svelte";
import type { ColorPickerProps } from "./ColorPicker";
import { withLazyProperties } from "anki/lazy";
import { dynamicComponent } from "sveltelib/dynamicComponent";
import * as tr from "anki/i18n";
import squareFillIcon from "./square-fill.svg";
@ -21,9 +23,9 @@ function wrapWithForecolor(color: string): void {
document.execCommand("forecolor", false, color);
}
const forecolorButton = withLazyProperties(
const iconButton = dynamicComponent(IconButton);
const forecolorButton = iconButton<IconButtonProps, "title">(
{
component: IconButton,
icon: squareFillIcon,
className: "forecolor",
onClick: () => wrapWithForecolor(getForecolor()),
@ -33,11 +35,12 @@ const forecolorButton = withLazyProperties(
}
);
const colorpickerButton = withLazyProperties(
const colorPicker = dynamicComponent(ColorPicker);
const colorpickerButton = colorPicker<ColorPickerProps, "title">(
{
component: ColorPicker,
className: "rainbow",
onChange: ({ currentTarget }) => setForegroundColor(currentTarget.value),
onChange: ({ currentTarget }) =>
setForegroundColor((currentTarget as HTMLInputElement).value),
},
{
title: tr.editingChangeColourF8,

View file

@ -1,6 +1,7 @@
import CommandIconButton from "./CommandIconButton.svelte";
import type { CommandIconButtonProps } from "./CommandIconButton";
import { withLazyProperties } from "anki/lazy";
import { dynamicComponent } from "sveltelib/dynamicComponent";
import * as tr from "anki/i18n";
import boldIcon from "./type-bold.svg";
@ -10,9 +11,10 @@ import superscriptIcon from "./format-superscript.svg";
import subscriptIcon from "./format-subscript.svg";
import eraserIcon from "./eraser.svg";
const boldButton = withLazyProperties(
const commandIconButton = dynamicComponent(CommandIconButton);
const boldButton = commandIconButton<CommandIconButtonProps, "title">(
{
component: CommandIconButton,
icon: boldIcon,
command: "bold",
},
@ -21,9 +23,8 @@ const boldButton = withLazyProperties(
}
);
const italicButton = withLazyProperties(
const italicButton = commandIconButton<CommandIconButtonProps, "title">(
{
component: CommandIconButton,
icon: italicIcon,
command: "italic",
},
@ -32,9 +33,8 @@ const italicButton = withLazyProperties(
}
);
const underlineButton = withLazyProperties(
const underlineButton = commandIconButton<CommandIconButtonProps, "title">(
{
component: CommandIconButton,
icon: underlineIcon,
command: "underline",
},
@ -43,9 +43,8 @@ const underlineButton = withLazyProperties(
}
);
const superscriptButton = withLazyProperties(
const superscriptButton = commandIconButton<CommandIconButtonProps, "title">(
{
component: CommandIconButton,
icon: superscriptIcon,
command: "superscript",
},
@ -54,9 +53,8 @@ const superscriptButton = withLazyProperties(
}
);
const subscriptButton = withLazyProperties(
const subscriptButton = commandIconButton<CommandIconButtonProps, "title">(
{
component: CommandIconButton,
icon: subscriptIcon,
command: "subscript",
},
@ -65,9 +63,8 @@ const subscriptButton = withLazyProperties(
}
);
const removeFormatButton = withLazyProperties(
const removeFormatButton = commandIconButton<CommandIconButtonProps, "title">(
{
component: CommandIconButton,
icon: eraserIcon,
command: "removeFormat",
activatable: false,

View file

@ -26,6 +26,7 @@ class EditorToolbar extends HTMLElement {
connectedCallback(): void {
setupI18n({ modules: [ModuleName.EDITING] }).then(() => {
console.log(this.buttons);
this.component = new EditorToolbarSvelte({
target: this,
props: {

View file

@ -1,12 +1,13 @@
import LabelButton from "./LabelButton.svelte";
import type { LabelButtonProps } from "./LabelButton";
import { dynamicComponent } from "sveltelib/dynamicComponent";
import { bridgeCommand } from "anki/bridgecommand";
import { withLazyProperties } from "anki/lazy";
import * as tr from "anki/i18n";
const fieldsButton = withLazyProperties(
const labelButton = dynamicComponent(LabelButton);
const fieldsButton = labelButton<LabelButtonProps, "label" | "title">(
{
component: LabelButton,
onClick: () => bridgeCommand("fields"),
disables: false,
},
@ -16,9 +17,8 @@ const fieldsButton = withLazyProperties(
}
);
const cardsButton = withLazyProperties(
const cardsButton = labelButton<LabelButtonProps, "label" | "title">(
{
component: LabelButton,
onClick: () => bridgeCommand("cards"),
disables: false,
},

View file

@ -1,10 +1,14 @@
import IconButton from "./IconButton.svelte";
import type { IconButtonProps } from "./IconButton";
import DropdownMenu from "./DropdownMenu.svelte";
import type { DropdownMenuProps } from "./DropdownMenu";
import DropdownItem from "./DropdownItem.svelte";
import type { DropdownItemProps } from "./DropdownItem";
import WithDropdownMenu from "./WithDropdownMenu.svelte";
import type { WithDropdownMenuProps } from "./WithDropdownMenu";
import { bridgeCommand } from "anki/bridgecommand";
import { withLazyProperties } from "anki/lazy";
import { dynamicComponent } from "sveltelib/dynamicComponent";
import * as tr from "anki/i18n";
import paperclipIcon from "./paperclip.svg";
@ -29,9 +33,14 @@ function onHtmlEdit(): void {
bridgeCommand("htmlEdit");
}
const attachmentButton = withLazyProperties(
const iconButton = dynamicComponent(IconButton);
const withDropdownMenu = dynamicComponent(WithDropdownMenu);
const dropdownMenu = dynamicComponent(DropdownMenu);
const dropdownItem = dynamicComponent(DropdownItem);
const attachmentButton = iconButton<IconButtonProps, "title">(
{
component: IconButton,
icon: paperclipIcon,
onClick: onAttachment,
},
@ -40,16 +49,15 @@ const attachmentButton = withLazyProperties(
}
);
const recordButton = withLazyProperties(
{ component: IconButton, icon: micIcon, onClick: onRecord },
const recordButton = iconButton(
{ icon: micIcon, onClick: onRecord },
{
title: tr.editingRecordAudioF5,
}
);
const clozeButton = withLazyProperties(
const clozeButton = iconButton<IconButtonProps, "title">(
{
component: IconButton,
icon: bracketsIcon,
onClick: onCloze,
},
@ -58,44 +66,58 @@ const clozeButton = withLazyProperties(
}
);
const mathjaxButton = {
component: IconButton,
icon: functionIcon,
};
const mathjaxButton = iconButton<Omit<IconButtonProps, "onClick" | "title">>(
{
icon: functionIcon,
},
{}
);
const mathjaxMenuId = "mathjaxMenu";
const mathjaxMenu = {
component: DropdownMenu,
id: mathjaxMenuId,
menuItems: [
withLazyProperties(
{ component: DropdownItem, onClick: () => bridgeCommand("mathjaxInline") },
{ label: tr.editingMathjaxInline }
),
withLazyProperties(
{ component: DropdownItem, onClick: () => bridgeCommand("mathjaxBlock") },
{ label: tr.editingMathjaxBlock }
),
withLazyProperties(
{
component: DropdownItem,
onClick: () => bridgeCommand("mathjaxChemistry"),
},
{ label: tr.editingMathjaxChemistry }
),
],
};
const mathjaxButtonWithMenu = {
component: WithDropdownMenu,
button: mathjaxButton,
menuId: mathjaxMenuId,
};
const htmlButton = withLazyProperties(
const mathjaxMenu = dropdownMenu<DropdownMenuProps>(
{
id: mathjaxMenuId,
menuItems: [
dropdownItem<DropdownItemProps, "label">(
{
onClick: () => bridgeCommand("mathjaxInline"),
title: "test",
endLabel: "test",
},
{ label: tr.editingMathjaxInline }
),
dropdownItem<DropdownItemProps, "label">(
{
onClick: () => bridgeCommand("mathjaxBlock"),
title: "test",
endLabel: "test",
},
{ label: tr.editingMathjaxBlock }
),
dropdownItem<DropdownItemProps, "label">(
{
onClick: () => bridgeCommand("mathjaxChemistry"),
title: "test",
endLabel: "test",
},
{ label: tr.editingMathjaxChemistry }
),
],
},
{}
);
const mathjaxButtonWithMenu = withDropdownMenu<WithDropdownMenuProps>(
{
button: mathjaxButton,
menuId: mathjaxMenuId,
},
{}
);
const htmlButton = iconButton<IconButtonProps, "title">(
{
component: IconButton,
icon: xmlIcon,
onClick: onHtmlEdit,
},

View file

@ -1,15 +1,8 @@
import type { SvelteComponent } from "svelte/internal";
import type { DynamicSvelteComponent } from "sveltelib/dynamicComponent";
export interface SvelteComponentDefinition {
component: SvelteComponent;
[arg: string]: unknown;
}
export interface ButtonDefinition extends SvelteComponentDefinition {
export interface ButtonDefinition extends DynamicSvelteComponent {
id?: string;
className?: string;
props?: Record<string, string>;
button: HTMLButtonElement;
}
export type Buttons = ButtonDefinition | Buttons[];

View file

@ -1,25 +0,0 @@
export function withLazyProperties(
object: Record<string, unknown>,
properties: Record<string, () => unknown>
): Record<string, unknown> {
const propertyDescriptorMap = Object.entries(properties)
.map(([name, getter]: [string, () => unknown]): [
string,
PropertyDescriptor
] => [
name,
{
get: getter,
enumerable: true,
},
])
.reduce(
(
accumulator: PropertyDescriptorMap,
[name, property]
): PropertyDescriptorMap => ((accumulator[name] = property), accumulator),
{}
);
return Object.defineProperties(object, propertyDescriptorMap);
}

View file

@ -0,0 +1,27 @@
import type { SvelteComponentDev } from "svelte/internal";
export interface DynamicSvelteComponent<
T extends typeof SvelteComponentDev = typeof SvelteComponentDev
> {
component: T;
}
export const dynamicComponent = <T extends typeof SvelteComponentDev>(component: T) => <
U extends NonNullable<ConstructorParameters<T>[0]["props"]>,
V extends string = never
>(
props: Omit<U, V>,
lazyProps: { [Property in keyof Pick<U, V>]: () => Pick<U, V>[Property] }
): DynamicSvelteComponent<T> & U => {
const dynamicComponent = { component, ...props };
for (const property in lazyProps) {
const get = lazyProps[property];
const propertyDescriptor: TypedPropertyDescriptor<
Pick<U, V>[Extract<keyof Pick<U, V>, string>]
> = { get, enumerable: true };
Object.defineProperty(dynamicComponent, property, propertyDescriptor);
}
return dynamicComponent as DynamicSvelteComponent<T> & U;
};