mirror of
https://github.com/ankitects/anki.git
synced 2025-09-24 16:56:36 -04: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"],
|
||||
),
|
||||
deps = [
|
||||
"//ts/sveltelib",
|
||||
"//ts/lib",
|
||||
"//ts/lib:backend_proto",
|
||||
"//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 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
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 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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
|
|
|
@ -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[];
|
||||
|
|
|
@ -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