From 296355fb6808131f1b66876700f36a548d52c14e Mon Sep 17 00:00:00 2001 From: Enteleform Date: Sun, 22 May 2022 07:05:30 -0400 Subject: [PATCH 1/5] add keyboard functionality for parent actions --- e2e/command-palette-basic.spec.ts | 9 +++++++++ src/app/views/demo/NestedActionDemo/nestedActions.ts | 1 + src/lib/CommandPalette.tsx | 2 +- src/lib/actionUtils/actionUtils.test.ts | 2 ++ src/lib/actionUtils/actionUtils.ts | 9 +++++++-- 5 files changed, 20 insertions(+), 3 deletions(-) diff --git a/e2e/command-palette-basic.spec.ts b/e2e/command-palette-basic.spec.ts index f0e681e..b4febfd 100644 --- a/e2e/command-palette-basic.spec.ts +++ b/e2e/command-palette-basic.spec.ts @@ -102,4 +102,13 @@ test.describe('Test basic interactions of Command Palette', () => { await expect(profileStatusLocator).toBeVisible(); }); + + test('should be able to open nested actions using keyboard', async ({ page }) => { + await page.goto('/demo'); + + await page.keyboard.press('p'); + await page.keyboard.press('o'); + + await expect(page.locator('.command-palette-portal [role="combobox"]')).toBeVisible(); + }); }); diff --git a/src/app/views/demo/NestedActionDemo/nestedActions.ts b/src/app/views/demo/NestedActionDemo/nestedActions.ts index 3c38d7f..e6a15bd 100644 --- a/src/app/views/demo/NestedActionDemo/nestedActions.ts +++ b/src/app/views/demo/NestedActionDemo/nestedActions.ts @@ -4,6 +4,7 @@ const setProfileAction = defineAction({ id: 'set-profile', title: 'Set profile', subtitle: 'Select this and then choose one of the options', + shortcut: 'p o', }); const setToPersonalProfileAction = defineAction({ diff --git a/src/lib/CommandPalette.tsx b/src/lib/CommandPalette.tsx index 25135fb..d923448 100644 --- a/src/lib/CommandPalette.tsx +++ b/src/lib/CommandPalette.tsx @@ -51,7 +51,7 @@ const CommandPaletteInternal: Component = (p) => { let lastFocusedElem: null | HTMLElement; function triggerRun(action: WrappedAction) { - runAction(action, state.actionsContext, storeMethods); + runAction(action, state.actionsContext, storeMethods, 'palette'); } function activatePrevItem() { diff --git a/src/lib/actionUtils/actionUtils.test.ts b/src/lib/actionUtils/actionUtils.test.ts index e5aa356..23ddef3 100644 --- a/src/lib/actionUtils/actionUtils.test.ts +++ b/src/lib/actionUtils/actionUtils.test.ts @@ -93,6 +93,7 @@ describe('Test Action Utils', () => { const runMock = vi.fn(); const selectParentActionMock = vi.fn(); const closePaletteMock = vi.fn(); + const openPaletteMock = vi.fn(); const baseAction = { id: 'test-action', @@ -103,6 +104,7 @@ describe('Test Action Utils', () => { const baseStoreMethods = { selectParentAction: selectParentActionMock, closePalette: closePaletteMock, + openPalette: openPaletteMock, }; afterEach(() => { diff --git a/src/lib/actionUtils/actionUtils.ts b/src/lib/actionUtils/actionUtils.ts index c710a92..6ba7afc 100644 --- a/src/lib/actionUtils/actionUtils.ts +++ b/src/lib/actionUtils/actionUtils.ts @@ -5,6 +5,7 @@ import { ActionId, ActionsContext, StoreMethods, WrappedAction, WrappedActionLis type RunStoreMethods = { selectParentAction: StoreMethods['selectParentAction']; closePalette: StoreMethods['closePalette']; + openPalette: StoreMethods['openPalette']; }; function getActionContext(action: WrappedAction, actionsContext: ActionsContext) { @@ -31,12 +32,16 @@ export function checkActionAllowed(action: WrappedAction, actionsContext: Action export function runAction( action: WrappedAction, actionsContext: ActionsContext, - storeMethods: RunStoreMethods + storeMethods: RunStoreMethods, + invokedBy: 'shortcut' | 'palette' ) { const { id, run } = action; if (!run) { storeMethods.selectParentAction(id); + if (invokedBy === 'shortcut') { + storeMethods.openPalette(); + } return; } @@ -68,7 +73,7 @@ export function getShortcutHandlersMap( } event.preventDefault(); - runAction(action, actionsContext, storeMethods); + runAction(action, actionsContext, storeMethods, 'shortcut'); }; const shortcut = action.shortcut; From f8254325eda3870db4f1c1d86de3a758158bf67e Mon Sep 17 00:00:00 2001 From: Enteleform Date: Sun, 22 May 2022 08:13:39 -0400 Subject: [PATCH 2/5] add `isolateChildren` functionality --- e2e/command-palette-basic.spec.ts | 14 +++++++++++++ .../demo/NestedActionDemo/nestedActions.ts | 1 + src/lib/actionUtils/actionUtils.ts | 7 ++++++- src/lib/createActionList.ts | 8 +++++-- src/lib/defineAction.ts | 4 +++- src/lib/types.ts | 21 ++++++++++++++++++- 6 files changed, 50 insertions(+), 5 deletions(-) diff --git a/e2e/command-palette-basic.spec.ts b/e2e/command-palette-basic.spec.ts index b4febfd..17a692c 100644 --- a/e2e/command-palette-basic.spec.ts +++ b/e2e/command-palette-basic.spec.ts @@ -111,4 +111,18 @@ test.describe('Test basic interactions of Command Palette', () => { await expect(page.locator('.command-palette-portal [role="combobox"]')).toBeVisible(); }); + + test('should not show child actions at root by default', async ({ page }) => { + await page.goto('/demo'); + await triggerCommandPaletteOpen(page); + + await expect(page.locator('text=Set to Personal profile')).not.toBeVisible(); + }); + + test('should show child actions at root with option', async ({ page }) => { + await page.goto('/demo'); + await triggerCommandPaletteOpen(page); + + await expect(page.locator('text=Configure Personal profile')).toBeVisible(); + }); }); diff --git a/src/app/views/demo/NestedActionDemo/nestedActions.ts b/src/app/views/demo/NestedActionDemo/nestedActions.ts index e6a15bd..3fda481 100644 --- a/src/app/views/demo/NestedActionDemo/nestedActions.ts +++ b/src/app/views/demo/NestedActionDemo/nestedActions.ts @@ -35,6 +35,7 @@ const configureProfileAction = defineAction({ id: 'configure-profile', title: 'Configure profile', subtitle: 'Select this to try 2 levels of nested actions', + isolateChildren: false, }); const configurePersonalProfileAction = defineAction({ diff --git a/src/lib/actionUtils/actionUtils.ts b/src/lib/actionUtils/actionUtils.ts index 6ba7afc..b306283 100644 --- a/src/lib/actionUtils/actionUtils.ts +++ b/src/lib/actionUtils/actionUtils.ts @@ -1,6 +1,7 @@ import { KeyBindingMap } from 'tinykeys'; import { rootParentActionId } from '../constants'; -import { ActionId, ActionsContext, StoreMethods, WrappedAction, WrappedActionList } from '../types'; +import { ActionId, Actions, ActionsContext, StoreMethods, WrappedAction, WrappedActionList } from '../types'; +import { DeepReadonly } from 'solid-js/store'; type RunStoreMethods = { selectParentAction: StoreMethods['selectParentAction']; @@ -87,6 +88,10 @@ export function getShortcutHandlersMap( type ActiveParentActionIdListArg = Readonly>; +export function getParentAction(action: WrappedAction, actions: DeepReadonly) { + return Object.values(actions).filter(({id}) => id === action.parentActionId)[0] +} + export function getActiveParentAction(activeParentActionIdList: ActiveParentActionIdListArg) { const activeId = activeParentActionIdList.at(-1) || rootParentActionId; const isRoot = activeId === rootParentActionId; diff --git a/src/lib/createActionList.ts b/src/lib/createActionList.ts index 7c2ee76..9afb982 100644 --- a/src/lib/createActionList.ts +++ b/src/lib/createActionList.ts @@ -1,7 +1,7 @@ import { createMemo, createEffect } from 'solid-js'; import Fuse from 'fuse.js'; import { useStore } from './StoreContext'; -import { checkActionAllowed, getActiveParentAction } from './actionUtils/actionUtils'; +import { checkActionAllowed, getParentAction, getActiveParentAction } from './actionUtils/actionUtils'; import { WrappedAction } from './types'; export function createActionList() { @@ -19,9 +19,13 @@ export function createNestedActionList() { const [state] = useStore(); function nestedActionFilter(action: WrappedAction) { + const parent = getParentAction(action, state.actions); const { activeId, isRoot } = getActiveParentAction(state.activeParentActionIdList); - const isAllowed = isRoot || action.parentActionId === activeId; + const showAtRoot = isRoot && !parent?.isolateChildren; + const isActiveChild = action.parentActionId === activeId; + const isAllowed = showAtRoot || isActiveChild; + return isAllowed; } diff --git a/src/lib/defineAction.ts b/src/lib/defineAction.ts index 2a47ad2..29b063b 100644 --- a/src/lib/defineAction.ts +++ b/src/lib/defineAction.ts @@ -8,6 +8,7 @@ export const defineAction = (partialAction: PartialAction): Action => { const keywords = partialAction.keywords || []; const shortcut = partialAction.shortcut || null; const run = partialAction.run; + const isolateChildren = partialAction.isolateChildren ?? true; const normalizedAction = { id, @@ -18,7 +19,8 @@ export const defineAction = (partialAction: PartialAction): Action => { shortcut, cond: partialAction.cond, run, - }; + isolateChildren, + } as Action; return normalizedAction; }; diff --git a/src/lib/types.ts b/src/lib/types.ts index 480375f..f4611ae 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -20,7 +20,7 @@ export interface RunArgs { dynamicContext: ActionContext; } -export interface Action { +export interface BaseAction { id: ActionId; parentActionId: ParentActionId; title: string; @@ -35,8 +35,27 @@ export interface Action { */ cond?: (args: RunArgs) => boolean; run?: (args: RunArgs) => void; + /** + * Prevent children from being displayed at the root level of the palette. + * + * Default: `true` + */ + isolateChildren?: boolean; +} + +export interface ChildAction { + isolateChildren?: never; } +export interface ParentAction { + parentActionId: never; + run?: never; +} + +export type Action = + | (Omit & ChildAction) + | (Omit & ParentAction) + export type PartialAction = Partial & { id: ActionId; title: Action['title']; From 5d95e4f136ff94325e547076cb44f50adfce7fac Mon Sep 17 00:00:00 2001 From: Enteleform Date: Mon, 30 May 2022 08:07:02 -0400 Subject: [PATCH 3/5] refactor `Action.isolateChildren` to `Root.initialVisibleActions` --- e2e/command-palette-basic.spec.ts | 30 ++----------------- ...lette-root-initial-visible-actions.spec.ts | 25 ++++++++++++++++ e2e/testUtils/triggerCommandPaletteOpen.ts | 12 ++++++++ src/app/App.tsx | 10 ++++++- src/app/views/demo/Default.view.tsx | 8 +++++ .../demo/InitialVisibleActions-All.view.tsx | 12 ++++++++ .../demo/InitialVisibleActions-Root.view.tsx | 12 ++++++++ .../CustomComponentsDemo.module.css | 0 .../CustomComponentsDemo.tsx | 4 +-- .../CustomComponentsDemo/components.ts | 0 .../views/demo/{ => shared}/Demo.module.css | 0 src/app/views/demo/{ => shared}/Demo.view.tsx | 10 +++++-- .../DynamicActionContextDemo.module.css | 0 .../DynamicActionContextDemo.tsx | 4 +-- .../DynamicActionContextDemo/data.ts | 0 .../dynamicContextActions.ts | 2 +- .../DynamicActionContextDemo/types.ts | 0 .../NestedActionDemo.module.css | 0 .../NestedActionDemo/NestedActionDemo.tsx | 4 +-- .../NestedActionDemo/nestedActions.ts | 5 ++-- src/app/views/demo/{ => shared}/actions.ts | 2 +- .../demo/{ => shared}/demoUtils.module.css | 0 src/app/views/demo/{ => shared}/types.ts | 0 src/lib/Root.tsx | 2 ++ src/lib/actionUtils/actionUtils.ts | 4 --- src/lib/createActionList.ts | 11 ++++--- src/lib/defineAction.ts | 2 -- src/lib/types.ts | 30 +++++++------------ 28 files changed, 117 insertions(+), 72 deletions(-) create mode 100644 e2e/command-palette-root-initial-visible-actions.spec.ts create mode 100644 e2e/testUtils/triggerCommandPaletteOpen.ts create mode 100644 src/app/views/demo/Default.view.tsx create mode 100644 src/app/views/demo/InitialVisibleActions-All.view.tsx create mode 100644 src/app/views/demo/InitialVisibleActions-Root.view.tsx rename src/app/views/demo/{ => shared}/CustomComponentsDemo/CustomComponentsDemo.module.css (100%) rename src/app/views/demo/{ => shared}/CustomComponentsDemo/CustomComponentsDemo.tsx (86%) rename src/app/views/demo/{ => shared}/CustomComponentsDemo/components.ts (100%) rename src/app/views/demo/{ => shared}/Demo.module.css (100%) rename src/app/views/demo/{ => shared}/Demo.view.tsx (93%) rename src/app/views/demo/{ => shared}/DynamicActionContextDemo/DynamicActionContextDemo.module.css (100%) rename src/app/views/demo/{ => shared}/DynamicActionContextDemo/DynamicActionContextDemo.tsx (98%) rename src/app/views/demo/{ => shared}/DynamicActionContextDemo/data.ts (100%) rename src/app/views/demo/{ => shared}/DynamicActionContextDemo/dynamicContextActions.ts (93%) rename src/app/views/demo/{ => shared}/DynamicActionContextDemo/types.ts (100%) rename src/app/views/demo/{ => shared}/NestedActionDemo/NestedActionDemo.module.css (100%) rename src/app/views/demo/{ => shared}/NestedActionDemo/NestedActionDemo.tsx (92%) rename src/app/views/demo/{ => shared}/NestedActionDemo/nestedActions.ts (97%) rename src/app/views/demo/{ => shared}/actions.ts (97%) rename src/app/views/demo/{ => shared}/demoUtils.module.css (100%) rename src/app/views/demo/{ => shared}/types.ts (100%) diff --git a/e2e/command-palette-basic.spec.ts b/e2e/command-palette-basic.spec.ts index 17a692c..a9475dc 100644 --- a/e2e/command-palette-basic.spec.ts +++ b/e2e/command-palette-basic.spec.ts @@ -1,15 +1,5 @@ -import { test, expect, Page } from '@playwright/test'; -import { checkMac } from './testUtils/checkMac'; - -async function triggerCommandPaletteOpen(page: Page) { - const isMac = await checkMac(page); - - if (isMac) { - await page.keyboard.press('Meta+k'); - } else { - await page.keyboard.press('Control+k'); - } -} +import { test, expect } from '@playwright/test'; +import { triggerCommandPaletteOpen } from './testUtils/triggerCommandPaletteOpen'; test.describe('Test basic interactions of Command Palette', () => { test('should be able to open command palette & run first action', async ({ page }) => { @@ -107,22 +97,8 @@ test.describe('Test basic interactions of Command Palette', () => { await page.goto('/demo'); await page.keyboard.press('p'); - await page.keyboard.press('o'); + await page.keyboard.press('s'); await expect(page.locator('.command-palette-portal [role="combobox"]')).toBeVisible(); }); - - test('should not show child actions at root by default', async ({ page }) => { - await page.goto('/demo'); - await triggerCommandPaletteOpen(page); - - await expect(page.locator('text=Set to Personal profile')).not.toBeVisible(); - }); - - test('should show child actions at root with option', async ({ page }) => { - await page.goto('/demo'); - await triggerCommandPaletteOpen(page); - - await expect(page.locator('text=Configure Personal profile')).toBeVisible(); - }); }); diff --git a/e2e/command-palette-root-initial-visible-actions.spec.ts b/e2e/command-palette-root-initial-visible-actions.spec.ts new file mode 100644 index 0000000..ea20aaa --- /dev/null +++ b/e2e/command-palette-root-initial-visible-actions.spec.ts @@ -0,0 +1,25 @@ +import { test, expect } from '@playwright/test'; +import { triggerCommandPaletteOpen } from './testUtils/triggerCommandPaletteOpen'; + +test.describe('Test `initialVisibleActions` prop of `Root` component', () => { + test('should not show nested actions at root by default', async ({ page }) => { + await page.goto('/demo'); + await triggerCommandPaletteOpen(page); + + await expect(page.locator('text=Set to Personal profile')).not.toBeVisible(); + }); + + test('should not show nested actions at root when set to `root`', async ({ page }) => { + await page.goto('/demo/InitialVisibleActions/Root'); + await triggerCommandPaletteOpen(page); + + await expect(page.locator('text=Configure Personal profile')).not.toBeVisible(); + }); + + test('should show nested actions at root when set to `all`', async ({ page }) => { + await page.goto('/demo/InitialVisibleActions/All'); + await triggerCommandPaletteOpen(page); + + await expect(page.locator('text=Configure Personal profile')).toBeVisible(); + }); +}); diff --git a/e2e/testUtils/triggerCommandPaletteOpen.ts b/e2e/testUtils/triggerCommandPaletteOpen.ts new file mode 100644 index 0000000..d97ac6e --- /dev/null +++ b/e2e/testUtils/triggerCommandPaletteOpen.ts @@ -0,0 +1,12 @@ +import { Page } from '@playwright/test'; +import { checkMac } from './checkMac'; + +export async function triggerCommandPaletteOpen(page: Page) { + const isMac = await checkMac(page); + + if (isMac) { + await page.keyboard.press('Meta+k'); + } else { + await page.keyboard.press('Control+k'); + } +} diff --git a/src/app/App.tsx b/src/app/App.tsx index f29ec6e..d2af248 100644 --- a/src/app/App.tsx +++ b/src/app/App.tsx @@ -46,7 +46,15 @@ const routes: Array = [ }, { path: '/demo', - component: lazy(() => import('./views/demo/Demo.view')), + component: lazy(() => import('./views/demo/Default.view')), + }, + { + path: '/demo/InitialVisibleActions/All', + component: lazy(() => import('./views/demo/InitialVisibleActions-All.view')), + }, + { + path: '/demo/InitialVisibleActions/Root', + component: lazy(() => import('./views/demo/InitialVisibleActions-Root.view')), }, { path: '/', diff --git a/src/app/views/demo/Default.view.tsx b/src/app/views/demo/Default.view.tsx new file mode 100644 index 0000000..b917583 --- /dev/null +++ b/src/app/views/demo/Default.view.tsx @@ -0,0 +1,8 @@ +import { Component } from 'solid-js'; +import BaseDemoView from './shared/Demo.view'; + +const DemoView: Component = () => { + return ; +} + +export default DemoView; diff --git a/src/app/views/demo/InitialVisibleActions-All.view.tsx b/src/app/views/demo/InitialVisibleActions-All.view.tsx new file mode 100644 index 0000000..5ef2223 --- /dev/null +++ b/src/app/views/demo/InitialVisibleActions-All.view.tsx @@ -0,0 +1,12 @@ +import { Component } from 'solid-js'; +import BaseDemoView from './shared/Demo.view'; + +const DemoView: Component = () => { + return ( + + ); +} + +export default DemoView; diff --git a/src/app/views/demo/InitialVisibleActions-Root.view.tsx b/src/app/views/demo/InitialVisibleActions-Root.view.tsx new file mode 100644 index 0000000..a241a43 --- /dev/null +++ b/src/app/views/demo/InitialVisibleActions-Root.view.tsx @@ -0,0 +1,12 @@ +import { Component } from 'solid-js'; +import BaseDemoView from './shared/Demo.view'; + +const DemoView: Component = () => { + return ( + + ); +} + +export default DemoView; diff --git a/src/app/views/demo/CustomComponentsDemo/CustomComponentsDemo.module.css b/src/app/views/demo/shared/CustomComponentsDemo/CustomComponentsDemo.module.css similarity index 100% rename from src/app/views/demo/CustomComponentsDemo/CustomComponentsDemo.module.css rename to src/app/views/demo/shared/CustomComponentsDemo/CustomComponentsDemo.module.css diff --git a/src/app/views/demo/CustomComponentsDemo/CustomComponentsDemo.tsx b/src/app/views/demo/shared/CustomComponentsDemo/CustomComponentsDemo.tsx similarity index 86% rename from src/app/views/demo/CustomComponentsDemo/CustomComponentsDemo.tsx rename to src/app/views/demo/shared/CustomComponentsDemo/CustomComponentsDemo.tsx index 285493f..64eb706 100644 --- a/src/app/views/demo/CustomComponentsDemo/CustomComponentsDemo.tsx +++ b/src/app/views/demo/shared/CustomComponentsDemo/CustomComponentsDemo.tsx @@ -1,6 +1,6 @@ import { Component, Show } from 'solid-js'; -import { KbdShortcut, ResultContentProps } from '../../../../lib'; -import utilStyles from '../../../utils.module.css'; +import { KbdShortcut, ResultContentProps } from '../../../../../lib'; +import utilStyles from '../../../../utils.module.css'; import styles from './CustomComponentsDemo.module.css'; export const DemoResultContent: Component = (p) => { diff --git a/src/app/views/demo/CustomComponentsDemo/components.ts b/src/app/views/demo/shared/CustomComponentsDemo/components.ts similarity index 100% rename from src/app/views/demo/CustomComponentsDemo/components.ts rename to src/app/views/demo/shared/CustomComponentsDemo/components.ts diff --git a/src/app/views/demo/Demo.module.css b/src/app/views/demo/shared/Demo.module.css similarity index 100% rename from src/app/views/demo/Demo.module.css rename to src/app/views/demo/shared/Demo.module.css diff --git a/src/app/views/demo/Demo.view.tsx b/src/app/views/demo/shared/Demo.view.tsx similarity index 93% rename from src/app/views/demo/Demo.view.tsx rename to src/app/views/demo/shared/Demo.view.tsx index bee3948..76e009b 100644 --- a/src/app/views/demo/Demo.view.tsx +++ b/src/app/views/demo/shared/Demo.view.tsx @@ -1,16 +1,19 @@ import { Component, createSignal, Show } from 'solid-js'; import { useSearchParams } from 'solid-app-router'; -import { Root, CommandPalette, KbdShortcut } from '../../../lib'; +import { Root, CommandPalette, KbdShortcut } from '../../../../lib'; +import { RootProps } from '../../../../lib/types'; import { actions } from './actions'; import { NestedActionDemo } from './NestedActionDemo/NestedActionDemo'; import { DynamicActionContextDemo } from './DynamicActionContextDemo/DynamicActionContextDemo'; import { components } from './CustomComponentsDemo/components'; import { Profile } from './types'; -import utilStyles from '../../utils.module.css'; +import utilStyles from '../../../utils.module.css'; import demoStyles from './demoUtils.module.css'; import styles from './Demo.module.css'; -const DemoView: Component = () => { +type DemoProps = Pick; + +const DemoView: Component = (p) => { const [count, setCount] = createSignal(0); const [muted, setMuted] = createSignal(false); const [profile, setProfile] = createSignal('personal'); @@ -66,6 +69,7 @@ const DemoView: Component = () => { actions={actions} actionsContext={actionsContext} components={customProps.components} + initialVisibleActions={p.initialVisibleActions} >
diff --git a/src/app/views/demo/DynamicActionContextDemo/DynamicActionContextDemo.module.css b/src/app/views/demo/shared/DynamicActionContextDemo/DynamicActionContextDemo.module.css similarity index 100% rename from src/app/views/demo/DynamicActionContextDemo/DynamicActionContextDemo.module.css rename to src/app/views/demo/shared/DynamicActionContextDemo/DynamicActionContextDemo.module.css diff --git a/src/app/views/demo/DynamicActionContextDemo/DynamicActionContextDemo.tsx b/src/app/views/demo/shared/DynamicActionContextDemo/DynamicActionContextDemo.tsx similarity index 98% rename from src/app/views/demo/DynamicActionContextDemo/DynamicActionContextDemo.tsx rename to src/app/views/demo/shared/DynamicActionContextDemo/DynamicActionContextDemo.tsx index 629b864..21e477c 100644 --- a/src/app/views/demo/DynamicActionContextDemo/DynamicActionContextDemo.tsx +++ b/src/app/views/demo/shared/DynamicActionContextDemo/DynamicActionContextDemo.tsx @@ -1,9 +1,9 @@ import { Component, createMemo, createSignal, createUniqueId, For, Show } from 'solid-js'; -import { KbdShortcut, createSyncActionsContext } from '../../../../lib'; +import { KbdShortcut, createSyncActionsContext } from '../../../../../lib'; import { ownContactId, contacts, contactActionId } from './data'; import { InputEventHandler, ContactItemProps, ReceiverContactDetailsProps } from './types'; import demoStyles from '../demoUtils.module.css'; -import utilStyles from '../../../utils.module.css'; +import utilStyles from '../../../../utils.module.css'; import styles from './DynamicActionContextDemo.module.css'; const ContactItem: Component = (p) => { diff --git a/src/app/views/demo/DynamicActionContextDemo/data.ts b/src/app/views/demo/shared/DynamicActionContextDemo/data.ts similarity index 100% rename from src/app/views/demo/DynamicActionContextDemo/data.ts rename to src/app/views/demo/shared/DynamicActionContextDemo/data.ts diff --git a/src/app/views/demo/DynamicActionContextDemo/dynamicContextActions.ts b/src/app/views/demo/shared/DynamicActionContextDemo/dynamicContextActions.ts similarity index 93% rename from src/app/views/demo/DynamicActionContextDemo/dynamicContextActions.ts rename to src/app/views/demo/shared/DynamicActionContextDemo/dynamicContextActions.ts index 70a1694..5e6a775 100644 --- a/src/app/views/demo/DynamicActionContextDemo/dynamicContextActions.ts +++ b/src/app/views/demo/shared/DynamicActionContextDemo/dynamicContextActions.ts @@ -1,4 +1,4 @@ -import { defineAction } from '../../../../lib'; +import { defineAction } from '../../../../../lib'; import { contactActionId, contacts } from './data'; export const contactAction = defineAction({ diff --git a/src/app/views/demo/DynamicActionContextDemo/types.ts b/src/app/views/demo/shared/DynamicActionContextDemo/types.ts similarity index 100% rename from src/app/views/demo/DynamicActionContextDemo/types.ts rename to src/app/views/demo/shared/DynamicActionContextDemo/types.ts diff --git a/src/app/views/demo/NestedActionDemo/NestedActionDemo.module.css b/src/app/views/demo/shared/NestedActionDemo/NestedActionDemo.module.css similarity index 100% rename from src/app/views/demo/NestedActionDemo/NestedActionDemo.module.css rename to src/app/views/demo/shared/NestedActionDemo/NestedActionDemo.module.css diff --git a/src/app/views/demo/NestedActionDemo/NestedActionDemo.tsx b/src/app/views/demo/shared/NestedActionDemo/NestedActionDemo.tsx similarity index 92% rename from src/app/views/demo/NestedActionDemo/NestedActionDemo.tsx rename to src/app/views/demo/shared/NestedActionDemo/NestedActionDemo.tsx index 81f8b32..1ecaa69 100644 --- a/src/app/views/demo/NestedActionDemo/NestedActionDemo.tsx +++ b/src/app/views/demo/shared/NestedActionDemo/NestedActionDemo.tsx @@ -1,8 +1,8 @@ import { Component } from 'solid-js'; -import { KbdShortcut } from '../../../../lib'; +import { KbdShortcut } from '../../../../../lib'; import { Profile } from '../types'; import demoStyles from '../demoUtils.module.css'; -import utilStyles from '../../../utils.module.css'; +import utilStyles from '../../../../utils.module.css'; import styles from './NestedActionDemo.module.css'; export interface Props { diff --git a/src/app/views/demo/NestedActionDemo/nestedActions.ts b/src/app/views/demo/shared/NestedActionDemo/nestedActions.ts similarity index 97% rename from src/app/views/demo/NestedActionDemo/nestedActions.ts rename to src/app/views/demo/shared/NestedActionDemo/nestedActions.ts index 3fda481..3a23866 100644 --- a/src/app/views/demo/NestedActionDemo/nestedActions.ts +++ b/src/app/views/demo/shared/NestedActionDemo/nestedActions.ts @@ -1,10 +1,10 @@ -import { defineAction } from '../../../../lib'; +import { defineAction } from '../../../../../lib'; const setProfileAction = defineAction({ id: 'set-profile', title: 'Set profile', subtitle: 'Select this and then choose one of the options', - shortcut: 'p o', + shortcut: 'p s', }); const setToPersonalProfileAction = defineAction({ @@ -35,7 +35,6 @@ const configureProfileAction = defineAction({ id: 'configure-profile', title: 'Configure profile', subtitle: 'Select this to try 2 levels of nested actions', - isolateChildren: false, }); const configurePersonalProfileAction = defineAction({ diff --git a/src/app/views/demo/actions.ts b/src/app/views/demo/shared/actions.ts similarity index 97% rename from src/app/views/demo/actions.ts rename to src/app/views/demo/shared/actions.ts index eb9ea78..f8e9537 100644 --- a/src/app/views/demo/actions.ts +++ b/src/app/views/demo/shared/actions.ts @@ -1,4 +1,4 @@ -import { defineAction } from '../../../lib'; +import { defineAction } from '../../../../lib'; import { contactAction } from './DynamicActionContextDemo/dynamicContextActions'; import { nestedActionsConfig } from './NestedActionDemo/nestedActions'; diff --git a/src/app/views/demo/demoUtils.module.css b/src/app/views/demo/shared/demoUtils.module.css similarity index 100% rename from src/app/views/demo/demoUtils.module.css rename to src/app/views/demo/shared/demoUtils.module.css diff --git a/src/app/views/demo/types.ts b/src/app/views/demo/shared/types.ts similarity index 100% rename from src/app/views/demo/types.ts rename to src/app/views/demo/shared/types.ts diff --git a/src/lib/Root.tsx b/src/lib/Root.tsx index 2c5878c..6702a4f 100644 --- a/src/lib/Root.tsx +++ b/src/lib/Root.tsx @@ -15,6 +15,7 @@ const RootInternal: Component = () => { export const Root: Component = (p) => { const initialActions = p.actions || {}; const initialActionsContext = p.actionsContext || {}; + const initialVisibleActions = p.initialVisibleActions || 'root'; const [state, setState] = createStore({ visibility: 'closed', @@ -26,6 +27,7 @@ export const Root: Component = (p) => { dynamic: {}, }, components: p.components, + initialVisibleActions: initialVisibleActions, }); const storeMethods: StoreMethods = { diff --git a/src/lib/actionUtils/actionUtils.ts b/src/lib/actionUtils/actionUtils.ts index b306283..9dff7a0 100644 --- a/src/lib/actionUtils/actionUtils.ts +++ b/src/lib/actionUtils/actionUtils.ts @@ -88,10 +88,6 @@ export function getShortcutHandlersMap( type ActiveParentActionIdListArg = Readonly>; -export function getParentAction(action: WrappedAction, actions: DeepReadonly) { - return Object.values(actions).filter(({id}) => id === action.parentActionId)[0] -} - export function getActiveParentAction(activeParentActionIdList: ActiveParentActionIdListArg) { const activeId = activeParentActionIdList.at(-1) || rootParentActionId; const isRoot = activeId === rootParentActionId; diff --git a/src/lib/createActionList.ts b/src/lib/createActionList.ts index 9afb982..074e8b5 100644 --- a/src/lib/createActionList.ts +++ b/src/lib/createActionList.ts @@ -1,7 +1,7 @@ import { createMemo, createEffect } from 'solid-js'; import Fuse from 'fuse.js'; import { useStore } from './StoreContext'; -import { checkActionAllowed, getParentAction, getActiveParentAction } from './actionUtils/actionUtils'; +import { checkActionAllowed, getActiveParentAction } from './actionUtils/actionUtils'; import { WrappedAction } from './types'; export function createActionList() { @@ -19,12 +19,15 @@ export function createNestedActionList() { const [state] = useStore(); function nestedActionFilter(action: WrappedAction) { - const parent = getParentAction(action, state.actions); const { activeId, isRoot } = getActiveParentAction(state.activeParentActionIdList); - const showAtRoot = isRoot && !parent?.isolateChildren; + const isRootAction = !action.parentActionId; const isActiveChild = action.parentActionId === activeId; - const isAllowed = showAtRoot || isActiveChild; + + const isAllowed = + (isRoot && isRootAction) + || isActiveChild + || state.initialVisibleActions === 'all' return isAllowed; } diff --git a/src/lib/defineAction.ts b/src/lib/defineAction.ts index 29b063b..20a3903 100644 --- a/src/lib/defineAction.ts +++ b/src/lib/defineAction.ts @@ -8,7 +8,6 @@ export const defineAction = (partialAction: PartialAction): Action => { const keywords = partialAction.keywords || []; const shortcut = partialAction.shortcut || null; const run = partialAction.run; - const isolateChildren = partialAction.isolateChildren ?? true; const normalizedAction = { id, @@ -19,7 +18,6 @@ export const defineAction = (partialAction: PartialAction): Action => { shortcut, cond: partialAction.cond, run, - isolateChildren, } as Action; return normalizedAction; diff --git a/src/lib/types.ts b/src/lib/types.ts index f4611ae..3856337 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -20,7 +20,7 @@ export interface RunArgs { dynamicContext: ActionContext; } -export interface BaseAction { +export interface Action { id: ActionId; parentActionId: ParentActionId; title: string; @@ -35,27 +35,8 @@ export interface BaseAction { */ cond?: (args: RunArgs) => boolean; run?: (args: RunArgs) => void; - /** - * Prevent children from being displayed at the root level of the palette. - * - * Default: `true` - */ - isolateChildren?: boolean; -} - -export interface ChildAction { - isolateChildren?: never; -} - -export interface ParentAction { - parentActionId: never; - run?: never; } -export type Action = - | (Omit & ChildAction) - | (Omit & ParentAction) - export type PartialAction = Partial & { id: ActionId; title: Action['title']; @@ -79,6 +60,7 @@ export interface RootProps { actions: Actions; actionsContext: ActionContext; components?: Components; + initialVisibleActions?: InitialVisibleActions; } export interface StoreState { @@ -88,6 +70,12 @@ export interface StoreState { actions: Actions; actionsContext: ActionsContext; components?: Components; + /** + * `root`: nested children are hidden from the root-level palette. (*default*) + * + * `all`: nested children are shown in the root-level palette. + */ + initialVisibleActions?: InitialVisibleActions; } export type StoreStateWrapped = Store; @@ -112,3 +100,5 @@ export type CreateSyncActionsContext = ( actionId: ActionId, callback: CreateSyncActionsContextCallback ) => void; + +export type InitialVisibleActions = 'root' | 'all' From 352bf53be12947b1c618b2233a579c1e9ab72b85 Mon Sep 17 00:00:00 2001 From: Enteleform Date: Mon, 30 May 2022 08:12:18 -0400 Subject: [PATCH 4/5] refactor inline `invokedBy` type to dedicated `InvokeBy` type --- src/lib/actionUtils/actionUtils.ts | 5 ++--- src/lib/types.ts | 2 ++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/lib/actionUtils/actionUtils.ts b/src/lib/actionUtils/actionUtils.ts index 9dff7a0..6c909fc 100644 --- a/src/lib/actionUtils/actionUtils.ts +++ b/src/lib/actionUtils/actionUtils.ts @@ -1,7 +1,6 @@ import { KeyBindingMap } from 'tinykeys'; import { rootParentActionId } from '../constants'; -import { ActionId, Actions, ActionsContext, StoreMethods, WrappedAction, WrappedActionList } from '../types'; -import { DeepReadonly } from 'solid-js/store'; +import { ActionId, ActionsContext, InvokeBy, StoreMethods, WrappedAction, WrappedActionList } from '../types'; type RunStoreMethods = { selectParentAction: StoreMethods['selectParentAction']; @@ -34,7 +33,7 @@ export function runAction( action: WrappedAction, actionsContext: ActionsContext, storeMethods: RunStoreMethods, - invokedBy: 'shortcut' | 'palette' + invokedBy: InvokeBy ) { const { id, run } = action; diff --git a/src/lib/types.ts b/src/lib/types.ts index 3856337..0b7118e 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -102,3 +102,5 @@ export type CreateSyncActionsContext = ( ) => void; export type InitialVisibleActions = 'root' | 'all' + +export type InvokeBy = 'shortcut' | 'palette' From 014f78b5219f69dd89a22662a64c19e048172e1f Mon Sep 17 00:00:00 2001 From: Enteleform Date: Thu, 2 Jun 2022 07:16:11 -0400 Subject: [PATCH 5/5] update `.gitignore` --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index b948412..7be701f 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ coverage .DS_Store test-results/ playwright-report/ +.vscode