mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Extract "composable-responsibilities" for Discriminable, Labelable, Orderable, and Showable
Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com>
This commit is contained in:
parent
5503b938b7
commit
f88de99511
@ -0,0 +1,7 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
// See: https://www.typescriptlang.org/docs/handbook/unions-and-intersections.html#discriminating-unions
|
||||
export interface Discriminable<T extends string> { kind: T }
|
||||
@ -0,0 +1,7 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
export interface Labelable {
|
||||
label: string;
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
export interface Orderable {
|
||||
orderNumber: number;
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { IComputedValue } from "mobx";
|
||||
import { isBoolean } from "../../type-narrowing";
|
||||
|
||||
export interface Showable<
|
||||
T extends IComputedValue<boolean> | boolean =
|
||||
| IComputedValue<boolean>
|
||||
| boolean,
|
||||
> {
|
||||
isShown?: T;
|
||||
}
|
||||
|
||||
export const isShown = (showable: Showable) => {
|
||||
if (showable.isShown === undefined) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isBoolean(showable.isShown)) {
|
||||
return showable.isShown;
|
||||
}
|
||||
|
||||
return showable.isShown.get();
|
||||
};
|
||||
17
src/common/utils/composite/interfaces.ts
Normal file
17
src/common/utils/composite/interfaces.ts
Normal file
@ -0,0 +1,17 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
export interface ParentOfChildComposite<Id extends string = string> {
|
||||
id: Id;
|
||||
}
|
||||
|
||||
export interface ChildOfParentComposite<Id extends string = string> {
|
||||
parentId: Id;
|
||||
}
|
||||
|
||||
export type RootComposite<Id extends string = string> =
|
||||
& { parentId: undefined }
|
||||
& ParentOfChildComposite<Id>;
|
||||
|
||||
@ -10,8 +10,14 @@ import { computed } from "mobx";
|
||||
import { pipeline } from "@ogre-tools/fp";
|
||||
import type { ApplicationMenuItemTypes } from "./menu-items/application-menu-item-injection-token";
|
||||
import loggerInjectable from "../../../common/logger.injectable";
|
||||
import type { RootComposite } from "../../../common/utils/composite/interfaces";
|
||||
import type { Discriminable } from "../../../common/utils/composable-responsibilities/discriminable/discriminable";
|
||||
import type { Orderable } from "../../../common/utils/composable-responsibilities/orderable/orderable";
|
||||
|
||||
export interface MenuItemRoot { id: "root"; parentId: undefined; kind: "root"; orderNumber: 0 }
|
||||
export type MenuItemRoot =
|
||||
& Discriminable<"root">
|
||||
& RootComposite<"root">
|
||||
& Orderable;
|
||||
|
||||
const applicationMenuItemCompositeInjectable = getInjectable({
|
||||
id: "application-menu-item-composite",
|
||||
|
||||
@ -5,8 +5,9 @@
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import type { MenuItemConstructorOptions } from "electron";
|
||||
import { computed } from "mobx";
|
||||
import applicationMenuItemInjectionToken, { isShown } from "./menu-items/application-menu-item-injection-token";
|
||||
import applicationMenuItemInjectionToken from "./menu-items/application-menu-item-injection-token";
|
||||
import { computedInjectManyInjectable } from "@ogre-tools/injectable-extension-for-mobx";
|
||||
import { isShown } from "../../../common/utils/composable-responsibilities/showable/showable";
|
||||
|
||||
export interface MenuItemOpts extends MenuItemConstructorOptions {
|
||||
submenu?: MenuItemConstructorOptions[];
|
||||
|
||||
@ -5,17 +5,16 @@
|
||||
import { getInjectionToken } from "@ogre-tools/injectable";
|
||||
import type { BrowserWindow, KeyboardEvent, MenuItemConstructorOptions, MenuItem as ElectronMenuItem } from "electron";
|
||||
import type { SetOptional } from "type-fest";
|
||||
import type { ChildOfParentComposite, ParentOfChildComposite } from "../../../../common/utils/composite/interfaces";
|
||||
import type { Showable } from "../../../../common/utils/composable-responsibilities/showable/showable";
|
||||
import type { Discriminable } from "../../../../common/utils/composable-responsibilities/discriminable/discriminable";
|
||||
import type { Orderable } from "../../../../common/utils/composable-responsibilities/orderable/orderable";
|
||||
|
||||
export interface MayHaveKeyboardShortcut {
|
||||
keyboardShortcut?: string;
|
||||
}
|
||||
|
||||
export interface Showable {
|
||||
isShown?: boolean;
|
||||
}
|
||||
export const isShown = (showable: Showable) => showable.isShown !== false;
|
||||
|
||||
export interface Clickable {
|
||||
export interface ElectronClickable {
|
||||
// TODO: This leaky abstraction is exposed in Extension API, therefore cannot be updated
|
||||
onClick: (menuItem: ElectronMenuItem, browserWindow: (BrowserWindow) | (undefined), event: KeyboardEvent) => void;
|
||||
}
|
||||
@ -26,29 +25,15 @@ export interface Labeled {
|
||||
|
||||
export interface MaybeLabeled extends SetOptional<Labeled, "label"> {}
|
||||
|
||||
export interface CanBeChildOfParent {
|
||||
parentId: string;
|
||||
}
|
||||
|
||||
export interface Orderable {
|
||||
orderNumber: number;
|
||||
}
|
||||
|
||||
export interface Identifiable {
|
||||
id: string;
|
||||
}
|
||||
|
||||
type ApplicationMenuItemType<T extends string> =
|
||||
// Note: "kind" is being used for Discriminated unions of TypeScript to achieve type narrowing.
|
||||
// See: https://www.typescriptlang.org/docs/handbook/2/narrowing.html#discriminated-unions
|
||||
& Kind<T>
|
||||
& Identifiable
|
||||
& CanBeChildOfParent
|
||||
& Discriminable<T>
|
||||
& ParentOfChildComposite
|
||||
& ChildOfParentComposite
|
||||
& Showable
|
||||
& Orderable;
|
||||
|
||||
interface Kind<T extends string> { kind: T }
|
||||
|
||||
export type TopLevelMenu =
|
||||
& ApplicationMenuItemType<"top-level-menu">
|
||||
& { parentId: "root" }
|
||||
@ -64,13 +49,13 @@ type ElectronRoles = Exclude<MenuItemConstructorOptions["role"], undefined>;
|
||||
export type SubMenu =
|
||||
& ApplicationMenuItemType<"sub-menu">
|
||||
& Labeled
|
||||
& CanBeChildOfParent;
|
||||
& ChildOfParentComposite;
|
||||
|
||||
export type ClickableMenuItem =
|
||||
& ApplicationMenuItemType<"clickable-menu-item">
|
||||
& MenuItem
|
||||
& Labeled
|
||||
& Clickable;
|
||||
& ElectronClickable;
|
||||
|
||||
export type OsActionMenuItem =
|
||||
& ApplicationMenuItemType<"os-action-menu-item">
|
||||
@ -79,7 +64,7 @@ export type OsActionMenuItem =
|
||||
& TriggersElectronAction;
|
||||
|
||||
type MenuItem =
|
||||
& CanBeChildOfParent
|
||||
& ChildOfParentComposite
|
||||
& MayHaveKeyboardShortcut;
|
||||
|
||||
interface TriggersElectronAction {
|
||||
@ -89,7 +74,7 @@ interface TriggersElectronAction {
|
||||
// Todo: SeparatorMenuItem
|
||||
export type Separator =
|
||||
& ApplicationMenuItemType<"separator">
|
||||
& CanBeChildOfParent;
|
||||
& ChildOfParentComposite;
|
||||
|
||||
export type ApplicationMenuItemTypes =
|
||||
| TopLevelMenu
|
||||
|
||||
@ -3,52 +3,54 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectionToken } from "@ogre-tools/injectable";
|
||||
import type { IComputedValue } from "mobx";
|
||||
import type React from "react";
|
||||
import type { ChildOfParentComposite, ParentOfChildComposite } from "../../../../common/utils/composite/interfaces";
|
||||
import type { Discriminable } from "../../../../common/utils/composable-responsibilities/discriminable/discriminable";
|
||||
import type { Labelable } from "../../../../common/utils/composable-responsibilities/labelable/labelable";
|
||||
import type { Showable } from "../../../../common/utils/composable-responsibilities/showable/showable";
|
||||
import type { Orderable } from "../../../../common/utils/composable-responsibilities/orderable/orderable";
|
||||
|
||||
export type PreferenceItemComponent<T> = React.ComponentType<{
|
||||
children: React.ReactElement;
|
||||
item: T;
|
||||
}>;
|
||||
|
||||
export interface PreferenceTab {
|
||||
kind: "tab";
|
||||
id: string;
|
||||
parentId: string;
|
||||
pathId: string;
|
||||
label: string;
|
||||
orderNumber: number;
|
||||
isShown?: IComputedValue<boolean> | boolean;
|
||||
}
|
||||
export type PreferenceTab =
|
||||
& Discriminable<"tab">
|
||||
& ParentOfChildComposite
|
||||
& ChildOfParentComposite
|
||||
& Showable
|
||||
& Labelable
|
||||
& Orderable
|
||||
& { pathId: string };
|
||||
|
||||
export interface PreferenceTabGroup {
|
||||
kind: "tab-group";
|
||||
id: string;
|
||||
parentId: "preference-tabs";
|
||||
label: string;
|
||||
orderNumber: number;
|
||||
isShown?: IComputedValue<boolean> | boolean;
|
||||
iconName?: string;
|
||||
}
|
||||
export type PreferenceTabGroup =
|
||||
& Discriminable<"tab-group">
|
||||
& ParentOfChildComposite
|
||||
& ChildOfParentComposite<"preference-tabs">
|
||||
& Showable
|
||||
& Labelable
|
||||
& Orderable
|
||||
& { iconName? : string };
|
||||
|
||||
export interface PreferencePage {
|
||||
kind: "page";
|
||||
id: string;
|
||||
parentId: string;
|
||||
isShown?: IComputedValue<boolean> | boolean;
|
||||
interface RenderableWithSiblings<T extends PreferenceTypes> {
|
||||
childSeparator?: () => React.ReactElement;
|
||||
Component: PreferenceItemComponent<PreferencePage>;
|
||||
Component: PreferenceItemComponent<T>;
|
||||
}
|
||||
|
||||
export interface PreferenceBlock {
|
||||
kind: "block";
|
||||
id: string;
|
||||
parentId: string;
|
||||
orderNumber: number;
|
||||
isShown?: IComputedValue<boolean> | boolean;
|
||||
childSeparator?: () => React.ReactElement;
|
||||
Component: PreferenceItemComponent<PreferenceBlock>;
|
||||
}
|
||||
export type PreferencePage =
|
||||
& Discriminable<"page">
|
||||
& ParentOfChildComposite
|
||||
& ChildOfParentComposite
|
||||
& Showable
|
||||
& RenderableWithSiblings<PreferencePage>;
|
||||
|
||||
export type PreferenceBlock =
|
||||
& Discriminable<"block">
|
||||
& ParentOfChildComposite
|
||||
& ChildOfParentComposite
|
||||
& Showable
|
||||
& RenderableWithSiblings<PreferenceBlock>;
|
||||
|
||||
export type PreferenceTypes = PreferenceTabGroup | PreferenceTab | PreferenceBlock | PreferencePage;
|
||||
|
||||
|
||||
@ -6,14 +6,15 @@ import type { IComputedValue } from "mobx";
|
||||
import { computed } from "mobx";
|
||||
import React from "react";
|
||||
import { HorizontalLine } from "../../../../renderer/components/horizontal-line/horizontal-line";
|
||||
import type { RootComposite } from "../../../../common/utils/composite/interfaces";
|
||||
import type { Discriminable } from "../../../../common/utils/composable-responsibilities/discriminable/discriminable";
|
||||
import type { Showable } from "../../../../common/utils/composable-responsibilities/showable/showable";
|
||||
|
||||
export interface PreferenceTabsRoot {
|
||||
kind: "preference-tabs-root";
|
||||
id: string;
|
||||
parentId: undefined;
|
||||
isShown: IComputedValue<true>;
|
||||
childSeparator: () => React.ReactElement;
|
||||
}
|
||||
export type PreferenceTabsRoot =
|
||||
& Discriminable<"preference-tabs-root">
|
||||
& RootComposite
|
||||
& Showable<IComputedValue<true>>
|
||||
& { childSeparator: () => React.ReactElement };
|
||||
|
||||
export const preferenceTabsRoot: PreferenceTabsRoot = {
|
||||
kind: "preference-tabs-root" as const,
|
||||
|
||||
@ -12,7 +12,7 @@ import { filter } from "lodash/fp";
|
||||
import { pipeline } from "@ogre-tools/fp";
|
||||
import { preferenceTabsRoot } from "./preference-tab-root";
|
||||
import logErrorInjectable from "../../../../common/log-error.injectable";
|
||||
import { isBoolean } from "../../../../common/utils";
|
||||
import { isShown } from "../../../../common/utils/composable-responsibilities/showable/showable";
|
||||
|
||||
const preferencesCompositeInjectable = getInjectable({
|
||||
id: "preferences-composite",
|
||||
@ -25,18 +25,7 @@ const preferencesCompositeInjectable = getInjectable({
|
||||
return computed(() =>
|
||||
pipeline(
|
||||
[preferenceTabsRoot, ...preferenceItems.get()],
|
||||
|
||||
filter((item: PreferenceTypes) => {
|
||||
if (item.isShown === undefined) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isBoolean(item.isShown)) {
|
||||
return item.isShown;
|
||||
}
|
||||
|
||||
return item.isShown.get();
|
||||
}),
|
||||
filter((item: PreferenceTypes) => isShown(item)),
|
||||
|
||||
(items) =>
|
||||
getComposite({
|
||||
|
||||
Loading…
Reference in New Issue
Block a user