Skip to content

Commit

Permalink
feat: open in editor command in search
Browse files Browse the repository at this point in the history
  • Loading branch information
Akryum committed Feb 9, 2023
1 parent 93287c5 commit c161d15
Show file tree
Hide file tree
Showing 11 changed files with 248 additions and 62 deletions.
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ module.exports = {
globals: {
name: 'off',
defineExpose: false,
__HISTOIRE_DEV__: false,
},
rules: {
'vue/html-closing-bracket-newline': [
Expand Down
30 changes: 30 additions & 0 deletions packages/histoire-app/src/app/components/base/BaseListItem.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<script lang="ts" setup>
defineProps<{
isActive?: boolean
}>()
const emit = defineEmits<{
(e: 'navigate'): void
}>()
function handleNavigate () {
emit('navigate')
}
</script>

<template>
<a
class="istoire-base-list-ite htw-flex htw-items-center htw-gap-2 htw-text-gray-900 dark:htw-text-gray-100"
:class="[
$attrs.class,
isActive
? 'active htw-bg-primary-500 hover:htw-bg-primary-600 htw-text-white dark:htw-text-black'
: 'hover:htw-bg-primary-100 dark:hover:htw-bg-primary-900'
]"
@click="handleNavigate()"
@keyup.enter="handleNavigate()"
@keyup.space="handleNavigate()"
>
<slot />
</a>
</template>
80 changes: 28 additions & 52 deletions packages/histoire-app/src/app/components/search/SearchItem.vue
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
<script lang="ts" setup>
import { PropType, ref, toRefs } from 'vue'
import { useRouter } from 'vue-router'
import { Icon } from '@iconify/vue'
import { SearchResult } from '../../types'
import { onKeyboardShortcut } from '../../util/keyboard'
import BaseListItemLink from '../base/BaseListItemLink.vue'
import { useScrollOnActive } from '../../util/scroll'
import BaseListItemLink from '../base/BaseListItemLink.vue'
import BaseListItem from '../base/BaseListItem.vue'
import SearchItemContent from './SearchItemContent.vue'
const props = defineProps({
result: {
Expand All @@ -32,18 +33,17 @@ const router = useRouter()
onKeyboardShortcut(['enter'], () => {
if (!props.selected) return
router.push(props.result.route)
emit('close')
action()
})
const defaultIcons = {
story: 'carbon:cube',
variant: 'carbon:cube',
}
const kindLabels = {
story: 'Story',
variant: 'Variant',
function action (fromClick = false) {
if ('route' in props.result && !fromClick) {
router.push(props.result.route)
}
if ('onActivate' in props.result) {
props.result.onActivate()
}
emit('close')
}
</script>

Expand All @@ -55,52 +55,28 @@ const kindLabels = {
:data-selected="selected ? '' : undefined"
>
<BaseListItemLink
v-if="'route' in result"
:to="result.route"
:is-active="selected"
class="htw-px-6 htw-py-4 htw-gap-4"
@navigate="$emit('close')"
@navigate="action(true)"
>
<Icon
:icon="result.icon ?? defaultIcons[result.kind]"
class="htw-w-4 htw-h-4"
:class="[
!selected ? [
result.iconColor
?'bind-icon-color'
: {
'htw-text-primary-500': result.kind === 'story',
'htw-text-gray-500': result.kind === 'variant',
}
] : [],
]"
<SearchItemContent
:result="result"
:selected="selected"
/>
<div class="htw-flex-1">
<div class="htw-flex">
{{ result.title }}
<span class="htw-ml-auto htw-opacity-40">
{{ kindLabels[result.kind] }}
</span>
</div>
<div
v-if="result.path?.length"
class="htw-flex htw-items-center htw-gap-0.5 htw-opacity-60"
>
<div
v-for="(p, index) of result.path"
:key="index"
class="htw-flex htw-items-center htw-gap-0.5"
>
<Icon
v-if="index > 0"
icon="carbon:chevron-right"
class="htw-w-4 htw-h-4 htw-mt-0.5 htw-opacity-50"
/>
<span>{{ p }}</span>
</div>
</div>
</div>
</BaseListItemLink>

<BaseListItem
v-if="'onActivate' in result"
class="htw-px-6 htw-py-4 htw-gap-4"
@navigate="action(true)"
>
<SearchItemContent
:result="result"
:selected="selected"
/>
</BaseListItem>
</div>
</template>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<script lang="ts" setup>
import { Icon } from '@iconify/vue'
import type { SearchResult } from '../../types.js'
defineProps<{
result: SearchResult
selected: boolean
}>()
const defaultIcons = {
story: 'carbon:cube',
variant: 'carbon:cube',
}
const kindLabels = {
story: 'Story',
variant: 'Variant',
command: 'Command',
}
</script>

<template>
<Icon
:icon="result.icon ?? defaultIcons[result.kind]"
class="htw-w-4 htw-h-4"
:class="[
!selected ? [
result.iconColor
?'bind-icon-color'
: {
'htw-text-primary-500': result.kind === 'story',
'htw-text-gray-500': result.kind === 'variant',
}
] : [],
]"
/>
<div class="htw-flex-1">
<div class="htw-flex">
{{ result.title }}
<span class="htw-ml-auto htw-opacity-40">
{{ kindLabels[result.kind] }}
</span>
</div>
<div
v-if="result.path?.length"
class="htw-flex htw-items-center htw-gap-0.5 htw-opacity-60"
>
<div
v-for="(p, index) of result.path"
:key="index"
class="htw-flex htw-items-center htw-gap-0.5"
>
<Icon
v-if="index > 0"
icon="carbon:chevron-right"
class="htw-w-4 htw-h-4 htw-mt-0.5 htw-opacity-50"
/>
<span>{{ p }}</span>
</div>
</div>
</div>
</template>
47 changes: 43 additions & 4 deletions packages/histoire-app/src/app/components/search/SearchPane.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ import { Icon } from '@iconify/vue'
import * as flexsearch from 'flexsearch'
import charset from 'flexsearch/dist/module/lang/latin/advanced.js'
import language from 'flexsearch/dist/module/lang/en.js'
import { useRoute } from 'vue-router'
import { useStoryStore } from '../../stores/story'
import BaseEmpty from '../base/BaseEmpty.vue'
import type { SearchResult, SearchResultType, Story, Variant } from '../../types'
import type { SearchCommand, SearchResult, SearchResultType, Story, Variant } from '../../types'
import SearchItem from './SearchItem.vue'
import { searchData, onUpdate } from './search-title-data'
import type { SearchData } from './types'
import { devCommands, executeCommand } from './dev-commands.js'
const DocSearchData = () => import('./search-docs-data')
Expand Down Expand Up @@ -194,7 +196,6 @@ function storyResultFactory (story: Story, rank: number, type: SearchResultType
path: story.file.path.slice(0, -1),
icon: story.icon,
iconColor: story.iconColor,
type,
}
}
Expand All @@ -219,12 +220,50 @@ function variantResultFactory (story: Story, variant: Variant, rank: number, typ
path: [...story.file.path ?? [], story.title],
icon: variant.icon,
iconColor: variant.iconColor,
type,
}
}
// Commands
const route = useRoute()
const commandResults = computed(() => {
if (__HISTOIRE_DEV__) {
const showIfCtx = {
route,
}
const searchText = searchInputText.value.toLowerCase()
return devCommands
.filter(command => !command.showIf || command.showIf(showIfCtx))
.filter(command => command.label.toLowerCase().includes(searchText) || command.id.toLowerCase().includes(searchText))
.map((command) => commandResultFactory(command, 0))
}
return []
})
function commandResultFactory (command: SearchCommand, rank: number): SearchResult {
return {
kind: 'command',
rank,
id: `_command:${command.id}`,
title: command.label,
icon: command.icon ?? 'carbon:chevron-right',
onActivate: () => {
const params = command.getParams?.({
route,
}) ?? {}
executeCommand(command, params)
},
}
}
// Results
const results = computed(() => {
const list = [...titleResults.value]
const list = [
...commandResults.value,
...titleResults.value,
]
const seen = {}
for (const r of titleResults.value) {
seen[r.id] = true
Expand Down
40 changes: 40 additions & 0 deletions packages/histoire-app/src/app/components/search/dev-commands.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { openInEditor } from '../../util/open-in-editor.js'
import { useStoryStore } from '../../stores/story.js'
import type { SearchCommand } from '../../types.js'

const storyStore = useStoryStore()

export const devCommands: SearchCommand[] = [
{
id: 'open-in-editor',
label: 'Open file in editor',
icon: 'carbon:script-reference',
showIf: ({ route }) => route.name === 'story' && !!storyStore.currentStory,
getParams: () => {
const story = storyStore.currentStory
let file: string
if (story.docsOnly) {
file = story.file?.docsFilePath ?? story.file?.filePath
} else {
file = story.file?.filePath
}
return {
file,
}
},
clientAction: ({ file }) => {
openInEditor(file)
},
},
]

export function executeCommand (command: SearchCommand, params: Record<string, any>) {
if (import.meta.hot) {
import.meta.hot.send('histoire:dev-command', {
id: command.id,
params,
})

command.clientAction?.(params)
}
}
31 changes: 25 additions & 6 deletions packages/histoire-app/src/app/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { RouteLocationRaw } from 'vue-router'
import type { RouteLocationRaw, RouteLocationNormalizedLoaded } from 'vue-router'
import type { Command } from '@histoire/shared'

export type {
StoryFile,
Expand Down Expand Up @@ -28,18 +29,23 @@ export type Tree = (TreeGroup | TreeFolder | TreeLeaf)[]

export type SearchResultType = 'title' | 'docs'

export interface SearchResult {
kind: 'story' | 'variant'
export interface SearchResultBase {
kind: 'story' | 'variant' | 'command'
rank: number
id: string
title: string
route: RouteLocationRaw
type: SearchResultType
path?: string[]
icon?: string
iconColor?: string
type?: SearchResultType
}

export type SearchResult = SearchResultBase & ({
route: RouteLocationRaw
} | {
onActivate: () => unknown
})

export interface PreviewSettings {
responsiveWidth: number
responsiveHeight: number
Expand All @@ -49,10 +55,23 @@ export interface PreviewSettings {
textDirection: 'ltr' | 'rtl'
}

export interface SearchCommand extends Command {
icon?: string
showIf?: (ctx: SearchCommandContext) => boolean
getParams?: (ctx: SearchCommandContext) => Record<string, any>
clientAction?: (params: Record<string, any>) => unknown
}

export interface SearchCommandContext {
route: RouteLocationNormalizedLoaded
}

declare module 'vue' {
interface ComponentCustomProperties {
__HISTOIRE_DEV__: boolean
}
}

declare const __HISTOIRE_DEV__: boolean
declare global {
const __HISTOIRE_DEV__: boolean
}
6 changes: 6 additions & 0 deletions packages/histoire-shared/src/types/command.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export type CommandId = 'open-in-editor'

export interface Command {
id: CommandId
label: string
}

0 comments on commit c161d15

Please sign in to comment.