mirror of
https://github.com/ankitects/anki.git
synced 2025-11-15 00:57:13 -05:00
Use dynamicComponent instead of withLazyProps
This commit is contained in:
parent
28201670ee
commit
c7590c8ef1
17 changed files with 170 additions and 100 deletions
|
|
@ -40,6 +40,7 @@ ts_library(
|
||||||
exclude = ["index.ts"],
|
exclude = ["index.ts"],
|
||||||
),
|
),
|
||||||
deps = [
|
deps = [
|
||||||
|
"//ts/sveltelib",
|
||||||
"//ts/lib",
|
"//ts/lib",
|
||||||
"//ts/lib:backend_proto",
|
"//ts/lib:backend_proto",
|
||||||
"//ts:image_module_support",
|
"//ts:image_module_support",
|
||||||
|
|
|
||||||
6
ts/editor-toolbar/ColorPicker.d.ts
vendored
Normal file
6
ts/editor-toolbar/ColorPicker.d.ts
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
export interface ColorPickerProps {
|
||||||
|
id?: string;
|
||||||
|
className?: string;
|
||||||
|
title: string;
|
||||||
|
onChange: (event: Event) => void;
|
||||||
|
}
|
||||||
8
ts/editor-toolbar/CommandIconButton.d.ts
vendored
Normal file
8
ts/editor-toolbar/CommandIconButton.d.ts
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
export interface CommandIconButtonProps {
|
||||||
|
id?: string;
|
||||||
|
className?: string;
|
||||||
|
title: string;
|
||||||
|
icon: string;
|
||||||
|
command: string;
|
||||||
|
activatable?: boolean;
|
||||||
|
}
|
||||||
|
|
@ -41,7 +41,7 @@
|
||||||
export let props: Record<string, string> = {};
|
export let props: Record<string, string> = {};
|
||||||
export let title: string;
|
export let title: string;
|
||||||
|
|
||||||
export let icon = "";
|
export let icon;
|
||||||
export let command: string;
|
export let command: string;
|
||||||
export let activatable = true;
|
export let activatable = true;
|
||||||
|
|
||||||
|
|
|
||||||
9
ts/editor-toolbar/DropdownItem.d.ts
vendored
Normal file
9
ts/editor-toolbar/DropdownItem.d.ts
vendored
Normal 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
6
ts/editor-toolbar/DropdownMenu.d.ts
vendored
Normal 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
7
ts/editor-toolbar/IconButton.d.ts
vendored
Normal 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
9
ts/editor-toolbar/LabelButton.d.ts
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
export interface LabelButtonProps {
|
||||||
|
id?: string;
|
||||||
|
className?: string;
|
||||||
|
|
||||||
|
label: string;
|
||||||
|
title: string;
|
||||||
|
onClick: (event: MouseEvent) => void;
|
||||||
|
disables?: boolean;
|
||||||
|
}
|
||||||
6
ts/editor-toolbar/WithDropdownMenu.d.ts
vendored
Normal file
6
ts/editor-toolbar/WithDropdownMenu.d.ts
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
import type { ButtonDefinition } from "./types";
|
||||||
|
|
||||||
|
export interface WithDropdownMenuProps {
|
||||||
|
button: ButtonDefinition;
|
||||||
|
menuId: string;
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
import IconButton from "./IconButton.svelte";
|
import IconButton from "./IconButton.svelte";
|
||||||
|
import type { IconButtonProps } from "./IconButton";
|
||||||
import ColorPicker from "./ColorPicker.svelte";
|
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 * as tr from "anki/i18n";
|
||||||
|
|
||||||
import squareFillIcon from "./square-fill.svg";
|
import squareFillIcon from "./square-fill.svg";
|
||||||
|
|
@ -21,9 +23,9 @@ function wrapWithForecolor(color: string): void {
|
||||||
document.execCommand("forecolor", false, color);
|
document.execCommand("forecolor", false, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
const forecolorButton = withLazyProperties(
|
const iconButton = dynamicComponent(IconButton);
|
||||||
|
const forecolorButton = iconButton<IconButtonProps, "title">(
|
||||||
{
|
{
|
||||||
component: IconButton,
|
|
||||||
icon: squareFillIcon,
|
icon: squareFillIcon,
|
||||||
className: "forecolor",
|
className: "forecolor",
|
||||||
onClick: () => wrapWithForecolor(getForecolor()),
|
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",
|
className: "rainbow",
|
||||||
onChange: ({ currentTarget }) => setForegroundColor(currentTarget.value),
|
onChange: ({ currentTarget }) =>
|
||||||
|
setForegroundColor((currentTarget as HTMLInputElement).value),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: tr.editingChangeColourF8,
|
title: tr.editingChangeColourF8,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import CommandIconButton from "./CommandIconButton.svelte";
|
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 * as tr from "anki/i18n";
|
||||||
|
|
||||||
import boldIcon from "./type-bold.svg";
|
import boldIcon from "./type-bold.svg";
|
||||||
|
|
@ -10,9 +11,10 @@ import superscriptIcon from "./format-superscript.svg";
|
||||||
import subscriptIcon from "./format-subscript.svg";
|
import subscriptIcon from "./format-subscript.svg";
|
||||||
import eraserIcon from "./eraser.svg";
|
import eraserIcon from "./eraser.svg";
|
||||||
|
|
||||||
const boldButton = withLazyProperties(
|
const commandIconButton = dynamicComponent(CommandIconButton);
|
||||||
|
|
||||||
|
const boldButton = commandIconButton<CommandIconButtonProps, "title">(
|
||||||
{
|
{
|
||||||
component: CommandIconButton,
|
|
||||||
icon: boldIcon,
|
icon: boldIcon,
|
||||||
command: "bold",
|
command: "bold",
|
||||||
},
|
},
|
||||||
|
|
@ -21,9 +23,8 @@ const boldButton = withLazyProperties(
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const italicButton = withLazyProperties(
|
const italicButton = commandIconButton<CommandIconButtonProps, "title">(
|
||||||
{
|
{
|
||||||
component: CommandIconButton,
|
|
||||||
icon: italicIcon,
|
icon: italicIcon,
|
||||||
command: "italic",
|
command: "italic",
|
||||||
},
|
},
|
||||||
|
|
@ -32,9 +33,8 @@ const italicButton = withLazyProperties(
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const underlineButton = withLazyProperties(
|
const underlineButton = commandIconButton<CommandIconButtonProps, "title">(
|
||||||
{
|
{
|
||||||
component: CommandIconButton,
|
|
||||||
icon: underlineIcon,
|
icon: underlineIcon,
|
||||||
command: "underline",
|
command: "underline",
|
||||||
},
|
},
|
||||||
|
|
@ -43,9 +43,8 @@ const underlineButton = withLazyProperties(
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const superscriptButton = withLazyProperties(
|
const superscriptButton = commandIconButton<CommandIconButtonProps, "title">(
|
||||||
{
|
{
|
||||||
component: CommandIconButton,
|
|
||||||
icon: superscriptIcon,
|
icon: superscriptIcon,
|
||||||
command: "superscript",
|
command: "superscript",
|
||||||
},
|
},
|
||||||
|
|
@ -54,9 +53,8 @@ const superscriptButton = withLazyProperties(
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const subscriptButton = withLazyProperties(
|
const subscriptButton = commandIconButton<CommandIconButtonProps, "title">(
|
||||||
{
|
{
|
||||||
component: CommandIconButton,
|
|
||||||
icon: subscriptIcon,
|
icon: subscriptIcon,
|
||||||
command: "subscript",
|
command: "subscript",
|
||||||
},
|
},
|
||||||
|
|
@ -65,9 +63,8 @@ const subscriptButton = withLazyProperties(
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const removeFormatButton = withLazyProperties(
|
const removeFormatButton = commandIconButton<CommandIconButtonProps, "title">(
|
||||||
{
|
{
|
||||||
component: CommandIconButton,
|
|
||||||
icon: eraserIcon,
|
icon: eraserIcon,
|
||||||
command: "removeFormat",
|
command: "removeFormat",
|
||||||
activatable: false,
|
activatable: false,
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ class EditorToolbar extends HTMLElement {
|
||||||
|
|
||||||
connectedCallback(): void {
|
connectedCallback(): void {
|
||||||
setupI18n({ modules: [ModuleName.EDITING] }).then(() => {
|
setupI18n({ modules: [ModuleName.EDITING] }).then(() => {
|
||||||
|
console.log(this.buttons);
|
||||||
this.component = new EditorToolbarSvelte({
|
this.component = new EditorToolbarSvelte({
|
||||||
target: this,
|
target: this,
|
||||||
props: {
|
props: {
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,13 @@
|
||||||
import LabelButton from "./LabelButton.svelte";
|
import LabelButton from "./LabelButton.svelte";
|
||||||
|
import type { LabelButtonProps } from "./LabelButton";
|
||||||
|
|
||||||
|
import { dynamicComponent } from "sveltelib/dynamicComponent";
|
||||||
import { bridgeCommand } from "anki/bridgecommand";
|
import { bridgeCommand } from "anki/bridgecommand";
|
||||||
import { withLazyProperties } from "anki/lazy";
|
|
||||||
import * as tr from "anki/i18n";
|
import * as tr from "anki/i18n";
|
||||||
|
|
||||||
const fieldsButton = withLazyProperties(
|
const labelButton = dynamicComponent(LabelButton);
|
||||||
|
const fieldsButton = labelButton<LabelButtonProps, "label" | "title">(
|
||||||
{
|
{
|
||||||
component: LabelButton,
|
|
||||||
onClick: () => bridgeCommand("fields"),
|
onClick: () => bridgeCommand("fields"),
|
||||||
disables: false,
|
disables: false,
|
||||||
},
|
},
|
||||||
|
|
@ -16,9 +17,8 @@ const fieldsButton = withLazyProperties(
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const cardsButton = withLazyProperties(
|
const cardsButton = labelButton<LabelButtonProps, "label" | "title">(
|
||||||
{
|
{
|
||||||
component: LabelButton,
|
|
||||||
onClick: () => bridgeCommand("cards"),
|
onClick: () => bridgeCommand("cards"),
|
||||||
disables: false,
|
disables: false,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,14 @@
|
||||||
import IconButton from "./IconButton.svelte";
|
import IconButton from "./IconButton.svelte";
|
||||||
|
import type { IconButtonProps } from "./IconButton";
|
||||||
import DropdownMenu from "./DropdownMenu.svelte";
|
import DropdownMenu from "./DropdownMenu.svelte";
|
||||||
|
import type { DropdownMenuProps } from "./DropdownMenu";
|
||||||
import DropdownItem from "./DropdownItem.svelte";
|
import DropdownItem from "./DropdownItem.svelte";
|
||||||
|
import type { DropdownItemProps } from "./DropdownItem";
|
||||||
import WithDropdownMenu from "./WithDropdownMenu.svelte";
|
import WithDropdownMenu from "./WithDropdownMenu.svelte";
|
||||||
|
import type { WithDropdownMenuProps } from "./WithDropdownMenu";
|
||||||
|
|
||||||
import { bridgeCommand } from "anki/bridgecommand";
|
import { bridgeCommand } from "anki/bridgecommand";
|
||||||
import { withLazyProperties } from "anki/lazy";
|
import { dynamicComponent } from "sveltelib/dynamicComponent";
|
||||||
import * as tr from "anki/i18n";
|
import * as tr from "anki/i18n";
|
||||||
|
|
||||||
import paperclipIcon from "./paperclip.svg";
|
import paperclipIcon from "./paperclip.svg";
|
||||||
|
|
@ -29,9 +33,14 @@ function onHtmlEdit(): void {
|
||||||
bridgeCommand("htmlEdit");
|
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,
|
icon: paperclipIcon,
|
||||||
onClick: onAttachment,
|
onClick: onAttachment,
|
||||||
},
|
},
|
||||||
|
|
@ -40,16 +49,15 @@ const attachmentButton = withLazyProperties(
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const recordButton = withLazyProperties(
|
const recordButton = iconButton(
|
||||||
{ component: IconButton, icon: micIcon, onClick: onRecord },
|
{ icon: micIcon, onClick: onRecord },
|
||||||
{
|
{
|
||||||
title: tr.editingRecordAudioF5,
|
title: tr.editingRecordAudioF5,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const clozeButton = withLazyProperties(
|
const clozeButton = iconButton<IconButtonProps, "title">(
|
||||||
{
|
{
|
||||||
component: IconButton,
|
|
||||||
icon: bracketsIcon,
|
icon: bracketsIcon,
|
||||||
onClick: onCloze,
|
onClick: onCloze,
|
||||||
},
|
},
|
||||||
|
|
@ -58,44 +66,58 @@ const clozeButton = withLazyProperties(
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const mathjaxButton = {
|
const mathjaxButton = iconButton<Omit<IconButtonProps, "onClick" | "title">>(
|
||||||
component: IconButton,
|
{
|
||||||
icon: functionIcon,
|
icon: functionIcon,
|
||||||
};
|
},
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
|
||||||
const mathjaxMenuId = "mathjaxMenu";
|
const mathjaxMenuId = "mathjaxMenu";
|
||||||
|
|
||||||
const mathjaxMenu = {
|
const mathjaxMenu = dropdownMenu<DropdownMenuProps>(
|
||||||
component: DropdownMenu,
|
{
|
||||||
id: mathjaxMenuId,
|
id: mathjaxMenuId,
|
||||||
menuItems: [
|
menuItems: [
|
||||||
withLazyProperties(
|
dropdownItem<DropdownItemProps, "label">(
|
||||||
{ component: DropdownItem, onClick: () => bridgeCommand("mathjaxInline") },
|
{
|
||||||
{ label: tr.editingMathjaxInline }
|
onClick: () => bridgeCommand("mathjaxInline"),
|
||||||
),
|
title: "test",
|
||||||
withLazyProperties(
|
endLabel: "test",
|
||||||
{ component: DropdownItem, onClick: () => bridgeCommand("mathjaxBlock") },
|
},
|
||||||
{ label: tr.editingMathjaxBlock }
|
{ label: tr.editingMathjaxInline }
|
||||||
),
|
),
|
||||||
withLazyProperties(
|
dropdownItem<DropdownItemProps, "label">(
|
||||||
{
|
{
|
||||||
component: DropdownItem,
|
onClick: () => bridgeCommand("mathjaxBlock"),
|
||||||
onClick: () => bridgeCommand("mathjaxChemistry"),
|
title: "test",
|
||||||
},
|
endLabel: "test",
|
||||||
{ label: tr.editingMathjaxChemistry }
|
},
|
||||||
),
|
{ label: tr.editingMathjaxBlock }
|
||||||
],
|
),
|
||||||
};
|
dropdownItem<DropdownItemProps, "label">(
|
||||||
|
{
|
||||||
const mathjaxButtonWithMenu = {
|
onClick: () => bridgeCommand("mathjaxChemistry"),
|
||||||
component: WithDropdownMenu,
|
title: "test",
|
||||||
button: mathjaxButton,
|
endLabel: "test",
|
||||||
menuId: mathjaxMenuId,
|
},
|
||||||
};
|
{ label: tr.editingMathjaxChemistry }
|
||||||
|
),
|
||||||
const htmlButton = withLazyProperties(
|
],
|
||||||
|
},
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
|
||||||
|
const mathjaxButtonWithMenu = withDropdownMenu<WithDropdownMenuProps>(
|
||||||
|
{
|
||||||
|
button: mathjaxButton,
|
||||||
|
menuId: mathjaxMenuId,
|
||||||
|
},
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
|
||||||
|
const htmlButton = iconButton<IconButtonProps, "title">(
|
||||||
{
|
{
|
||||||
component: IconButton,
|
|
||||||
icon: xmlIcon,
|
icon: xmlIcon,
|
||||||
onClick: onHtmlEdit,
|
onClick: onHtmlEdit,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,8 @@
|
||||||
import type { SvelteComponent } from "svelte/internal";
|
import type { DynamicSvelteComponent } from "sveltelib/dynamicComponent";
|
||||||
|
|
||||||
export interface SvelteComponentDefinition {
|
export interface ButtonDefinition extends DynamicSvelteComponent {
|
||||||
component: SvelteComponent;
|
|
||||||
[arg: string]: unknown;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ButtonDefinition extends SvelteComponentDefinition {
|
|
||||||
id?: string;
|
id?: string;
|
||||||
className?: string;
|
className?: string;
|
||||||
props?: Record<string, string>;
|
|
||||||
button: HTMLButtonElement;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Buttons = ButtonDefinition | Buttons[];
|
export type Buttons = ButtonDefinition | Buttons[];
|
||||||
|
|
|
||||||
|
|
@ -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);
|
|
||||||
}
|
|
||||||
27
ts/sveltelib/dynamicComponent.ts
Normal file
27
ts/sveltelib/dynamicComponent.ts
Normal 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;
|
||||||
|
};
|
||||||
Loading…
Reference in a new issue