power-compass 0.0.1-alpha.1 → 0.0.1-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/power-compass.cjs.js +20 -0
- package/dist/power-compass.es.d.ts +1 -0
- package/dist/power-compass.es.js +4831 -0
- package/package.json +12 -5
- package/.eslintrc.json +0 -6
- package/index.html +0 -13
- package/jest.config.js +0 -11
- package/src/components/context.ts +0 -9
- package/src/components/hooks/useCursor.ts +0 -38
- package/src/components/hooks/useDebounce.ts +0 -14
- package/src/components/hooks/useShortcut.ts +0 -18
- package/src/http/fetch.ts +0 -30
- package/src/http/index.ts +0 -1
- package/src/index.ts +0 -5
- package/src/menu/clientMenu.ts +0 -56
- package/src/menu/fetchMenu.spec.ts +0 -183
- package/src/menu/fetchMenu.ts +0 -42
- package/src/menu/index.ts +0 -2
- package/src/menu/transformations.ts +0 -48
- package/src/menu/types.ts +0 -19
- package/src/menu/useCompassMenu.spec.tsx +0 -173
- package/src/menu/useCompassMenu.ts +0 -67
- package/src/search/engine.ts +0 -109
- package/src/search/index.ts +0 -1
- package/src/search/sources.ts +0 -232
- package/src/search/types.ts +0 -39
- package/src/search/useSearch.ts +0 -84
- package/src/tasks/index.ts +0 -1
- package/src/tasks/types.ts +0 -7
- package/src/tasks/useLocalStorage.ts +0 -31
- package/src/tasks/useNotificationCenter.ts +0 -69
- package/tsconfig.app.json +0 -28
- package/tsconfig.json +0 -7
- package/tsconfig.node.json +0 -26
- package/vite.config.ts +0 -34
- package/vitest.config.ts +0 -8
- package/vitest.fetch-mock.ts +0 -7
package/package.json
CHANGED
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "power-compass",
|
|
3
|
-
"version": "0.0.1-alpha.
|
|
3
|
+
"version": "0.0.1-alpha.3",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"main": "./dist/compass.cjs.js",
|
|
6
|
-
"module": "./dist/compass.es.js",
|
|
5
|
+
"main": "./dist/power-compass.cjs.js",
|
|
6
|
+
"module": "./dist/power-compass.es.js",
|
|
7
|
+
"types": "./dist/power-compass.es.d.ts",
|
|
7
8
|
"exports": {
|
|
8
9
|
".": {
|
|
9
|
-
"import": "./dist/compass.es.js",
|
|
10
|
-
"require": "./dist/compass.cjs.js"
|
|
10
|
+
"import": "./dist/power-compass.es.js",
|
|
11
|
+
"require": "./dist/power-compass.cjs.js",
|
|
12
|
+
"types": "./dist/power-compass.es.d.ts"
|
|
13
|
+
},
|
|
14
|
+
"./cjs": {
|
|
15
|
+
"require": "./dist/power-compass.cjs.js",
|
|
16
|
+
"types": "./dist/power-compass.es.d.ts"
|
|
11
17
|
}
|
|
12
18
|
},
|
|
13
19
|
"scripts": {
|
|
@@ -35,6 +41,7 @@
|
|
|
35
41
|
"devDependencies": {
|
|
36
42
|
"@powerhome/eslint-config": "0.3.0",
|
|
37
43
|
"@testing-library/react-hooks": "^8.0.1",
|
|
44
|
+
"@types/lodash": "^4.17.23",
|
|
38
45
|
"@types/react": "^19.2.9",
|
|
39
46
|
"@typescript-eslint/eslint-plugin": "8.18.0",
|
|
40
47
|
"@typescript-eslint/parser": "8.18.0",
|
package/.eslintrc.json
DELETED
package/index.html
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8" />
|
|
5
|
-
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
|
6
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
-
<title>power-compass</title>
|
|
8
|
-
</head>
|
|
9
|
-
<body>
|
|
10
|
-
<div id="root"></div>
|
|
11
|
-
<script type="module" src="/src/main.tsx"></script>
|
|
12
|
-
</body>
|
|
13
|
-
</html>
|
package/jest.config.js
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
export default {
|
|
2
|
-
automock: false,
|
|
3
|
-
maxWorkers: 5,
|
|
4
|
-
preset: "ts-jest",
|
|
5
|
-
testEnvironment: "jest-environment-jsdom",
|
|
6
|
-
transformIgnorePatterns: ["/node_modules/(?!playbook-ui)"],
|
|
7
|
-
transform: {
|
|
8
|
-
"^.+\\.tsx?$": ["ts-jest", { tsconfig: "./tsconfig.json" }],
|
|
9
|
-
"^.+\\.scss$": "jest-scss-transform",
|
|
10
|
-
},
|
|
11
|
-
}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import React, { useState, useEffect } from "react"
|
|
2
|
-
import findIndex from "lodash/findIndex"
|
|
3
|
-
import nth from "lodash/nth"
|
|
4
|
-
import flatMap from "lodash/flatMap"
|
|
5
|
-
import get from "lodash/get"
|
|
6
|
-
|
|
7
|
-
import type { SearchResult, ResultEntry } from "../types"
|
|
8
|
-
|
|
9
|
-
type Cursor = {
|
|
10
|
-
cursor?: ResultEntry<unknown>
|
|
11
|
-
previous: () => void
|
|
12
|
-
next: () => void
|
|
13
|
-
resetCursor: () => void
|
|
14
|
-
updateCursor: React.Dispatch<
|
|
15
|
-
React.SetStateAction<ResultEntry<unknown> | undefined>
|
|
16
|
-
>
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function useCursor(searchResult: SearchResult<unknown>[]): Cursor {
|
|
20
|
-
const [cursor, updateCursor] = useState<ResultEntry<unknown> | undefined>()
|
|
21
|
-
useEffect(() => {
|
|
22
|
-
updateCursor(get(searchResult, "0.entries.0"))
|
|
23
|
-
}, [searchResult])
|
|
24
|
-
|
|
25
|
-
const entries = flatMap(searchResult, "entries")
|
|
26
|
-
const moveCursor = (positions: number) => {
|
|
27
|
-
const currentIndex = cursor ? findIndex(entries, cursor) : -1
|
|
28
|
-
const nextIndex = (currentIndex + positions) % entries.length
|
|
29
|
-
updateCursor(nth(entries, nextIndex))
|
|
30
|
-
}
|
|
31
|
-
const previous = () => moveCursor(-1)
|
|
32
|
-
const next = () => moveCursor(1)
|
|
33
|
-
const resetCursor = () => updateCursor(undefined)
|
|
34
|
-
|
|
35
|
-
return { cursor, previous, next, resetCursor, updateCursor }
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export default useCursor
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { useState, useEffect } from "react"
|
|
2
|
-
|
|
3
|
-
const useDebounce = (value: string, delay = 300) => {
|
|
4
|
-
const [debouncedValue, updateDebouncedValue] = useState(value)
|
|
5
|
-
|
|
6
|
-
useEffect(() => {
|
|
7
|
-
const handler = setTimeout(() => updateDebouncedValue(value), delay)
|
|
8
|
-
return () => clearTimeout(handler)
|
|
9
|
-
}, [value, delay])
|
|
10
|
-
|
|
11
|
-
return debouncedValue
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export default useDebounce
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { useEffect } from "react"
|
|
2
|
-
|
|
3
|
-
const useShortcut = (shortcut: string, ref?: HTMLInputElement) => {
|
|
4
|
-
useEffect(() => {
|
|
5
|
-
const handler = (event: KeyboardEvent) => {
|
|
6
|
-
const modifier = event.ctrlKey || event.metaKey
|
|
7
|
-
if (modifier && shortcut === event.key.toLowerCase()) {
|
|
8
|
-
event.preventDefault()
|
|
9
|
-
event.stopPropagation()
|
|
10
|
-
ref?.focus()
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
document.body.addEventListener("keydown", handler)
|
|
14
|
-
return () => document.body.removeEventListener("keydown", handler)
|
|
15
|
-
}, [shortcut, ref])
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export default useShortcut
|
package/src/http/fetch.ts
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
export async function fetchBackend<T>(
|
|
2
|
-
backend: string,
|
|
3
|
-
contextId: string,
|
|
4
|
-
service: string,
|
|
5
|
-
query: ConstructorParameters<typeof URLSearchParams>[0],
|
|
6
|
-
): Promise<T> {
|
|
7
|
-
const url = new URL(
|
|
8
|
-
`${backend}/${contextId}/${service}`,
|
|
9
|
-
window.location.origin,
|
|
10
|
-
)
|
|
11
|
-
url.search = new URLSearchParams(query).toString()
|
|
12
|
-
|
|
13
|
-
console.debug("Fetching data from backend:", url.toString()) // eslint-disable-line no-console
|
|
14
|
-
|
|
15
|
-
const respond = (response: Response) => {
|
|
16
|
-
if (response.ok) {
|
|
17
|
-
return response.json()
|
|
18
|
-
} else {
|
|
19
|
-
throw new Error(`${backend} reponse error: ${response.statusText}`)
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
return fetch(url.toString(), { cache: "default" })
|
|
24
|
-
.then(respond)
|
|
25
|
-
.catch(() => fetch(url.toString(), { cache: "force-cache" }).then(respond))
|
|
26
|
-
.catch((error) => {
|
|
27
|
-
console.debug(`Failed to fetch from ${backend}:`, error) // eslint-disable-line no-console
|
|
28
|
-
throw error
|
|
29
|
-
})
|
|
30
|
-
}
|
package/src/http/index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./fetch"
|
package/src/index.ts
DELETED
package/src/menu/clientMenu.ts
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import type { MenuItem, CompassMenuResult } from "./types"
|
|
2
|
-
|
|
3
|
-
export type FutureMenuItem = () => Promise<MenuItem>
|
|
4
|
-
export type FilterValue = true | false | undefined | string
|
|
5
|
-
export type PluginFilterKey = keyof typeof PluginFilters
|
|
6
|
-
export type PluginFilter = {
|
|
7
|
-
[Property in PluginFilterKey]?: FilterValue
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const PluginMenuItems: (MenuItem | Promise<MenuItem>)[] = []
|
|
11
|
-
const PluginFilters = {
|
|
12
|
-
tagged(items: MenuItem[], tag: FilterValue): MenuItem[] {
|
|
13
|
-
return items.filter((item) => item.tags?.includes(tag as string))
|
|
14
|
-
},
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function applyFilters(allItems: MenuItem[], filters: PluginFilter): MenuItem[] {
|
|
18
|
-
const filterKeys = Object.keys(filters) as PluginFilterKey[]
|
|
19
|
-
|
|
20
|
-
return filterKeys.reduce(
|
|
21
|
-
(items: MenuItem[], filter: PluginFilterKey): MenuItem[] =>
|
|
22
|
-
PluginFilters[filter](items, filters[filter]),
|
|
23
|
-
allItems,
|
|
24
|
-
)
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export function fetchPlugin(
|
|
28
|
-
filters: PluginFilter = {},
|
|
29
|
-
): Promise<CompassMenuResult> {
|
|
30
|
-
return new Promise((resolve) => {
|
|
31
|
-
const items = PluginMenuItems.map((item) => Promise.resolve(item))
|
|
32
|
-
|
|
33
|
-
Promise.all(items)
|
|
34
|
-
.then((resolvedItems) => resolvedItems.filter(Boolean))
|
|
35
|
-
.then((resolvedItems) => applyFilters(resolvedItems, filters))
|
|
36
|
-
.then((filteredItems) => resolve({ items: filteredItems }))
|
|
37
|
-
.catch((error) =>
|
|
38
|
-
resolve({
|
|
39
|
-
items: [],
|
|
40
|
-
error: {
|
|
41
|
-
message: error instanceof Error ? error.message : String(error),
|
|
42
|
-
},
|
|
43
|
-
}),
|
|
44
|
-
)
|
|
45
|
-
})
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export async function createMenuItem(
|
|
49
|
-
item: MenuItem | FutureMenuItem,
|
|
50
|
-
): Promise<void> {
|
|
51
|
-
if (typeof item === "function") {
|
|
52
|
-
PluginMenuItems.push(item())
|
|
53
|
-
} else {
|
|
54
|
-
PluginMenuItems.push(item)
|
|
55
|
-
}
|
|
56
|
-
}
|
|
@@ -1,183 +0,0 @@
|
|
|
1
|
-
/* global fetchMock */
|
|
2
|
-
|
|
3
|
-
import { fetchMenu, fetchMenus } from "./fetchMenu"
|
|
4
|
-
import type { MenuItem } from "../menu/types"
|
|
5
|
-
import { expect, describe, beforeEach, it } from "vitest"
|
|
6
|
-
|
|
7
|
-
const currentUserId = "123"
|
|
8
|
-
|
|
9
|
-
describe("Compass Menu fetch", () => {
|
|
10
|
-
beforeEach(() => {
|
|
11
|
-
fetchMock.resetMocks()
|
|
12
|
-
})
|
|
13
|
-
|
|
14
|
-
describe("fetchMenu", () => {
|
|
15
|
-
it("fetches the menu items", async () => {
|
|
16
|
-
const items: MenuItem[] = [
|
|
17
|
-
{
|
|
18
|
-
label: "Test",
|
|
19
|
-
url: "http://example.com/test1",
|
|
20
|
-
icon: "test1",
|
|
21
|
-
},
|
|
22
|
-
]
|
|
23
|
-
|
|
24
|
-
fetchMock.mockIf(
|
|
25
|
-
`http://example.com/compass/${currentUserId}/menu`,
|
|
26
|
-
JSON.stringify(items),
|
|
27
|
-
)
|
|
28
|
-
|
|
29
|
-
const fetchResult = await fetchMenu(
|
|
30
|
-
"http://example.com/compass",
|
|
31
|
-
currentUserId,
|
|
32
|
-
)
|
|
33
|
-
|
|
34
|
-
expect(fetchResult).toEqual({ items })
|
|
35
|
-
})
|
|
36
|
-
|
|
37
|
-
it("fetches menu items with the given filters", async () => {
|
|
38
|
-
const items: MenuItem[] = [
|
|
39
|
-
{
|
|
40
|
-
label: "Test",
|
|
41
|
-
url: "http://example.com/test1",
|
|
42
|
-
icon: "test1",
|
|
43
|
-
},
|
|
44
|
-
]
|
|
45
|
-
|
|
46
|
-
fetchMock.mockIf(
|
|
47
|
-
`http://example.com/compass/${currentUserId}/menu?tagged=actions`,
|
|
48
|
-
JSON.stringify(items),
|
|
49
|
-
)
|
|
50
|
-
|
|
51
|
-
const fetchResult = await fetchMenu(
|
|
52
|
-
"http://example.com/compass",
|
|
53
|
-
currentUserId,
|
|
54
|
-
{ tagged: "actions" },
|
|
55
|
-
)
|
|
56
|
-
|
|
57
|
-
expect(fetchResult).toEqual({ items })
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
it("fetches the menu items from local cache when the network is down", async () => {
|
|
61
|
-
const items: MenuItem[] = [
|
|
62
|
-
{
|
|
63
|
-
label: "Test",
|
|
64
|
-
url: "http://example.com/test1",
|
|
65
|
-
icon: "test1",
|
|
66
|
-
},
|
|
67
|
-
]
|
|
68
|
-
|
|
69
|
-
fetchMock
|
|
70
|
-
.mockImplementationOnce((url, options) => {
|
|
71
|
-
expect(url).toEqual(
|
|
72
|
-
`http://example.com/compass/${currentUserId}/menu`,
|
|
73
|
-
)
|
|
74
|
-
expect(options.cache).toEqual("default")
|
|
75
|
-
|
|
76
|
-
return Promise.resolve(new Response(null, { status: 404 }))
|
|
77
|
-
})
|
|
78
|
-
.mockImplementationOnce((url, options) => {
|
|
79
|
-
expect(url).toEqual(
|
|
80
|
-
`http://example.com/compass/${currentUserId}/menu`,
|
|
81
|
-
)
|
|
82
|
-
expect(options.cache).toEqual("force-cache")
|
|
83
|
-
|
|
84
|
-
return Promise.resolve(new Response(JSON.stringify(items)))
|
|
85
|
-
})
|
|
86
|
-
|
|
87
|
-
const fetchResult = await fetchMenu(
|
|
88
|
-
"http://example.com/compass",
|
|
89
|
-
currentUserId,
|
|
90
|
-
)
|
|
91
|
-
|
|
92
|
-
expect(fetchResult).toEqual({ items })
|
|
93
|
-
})
|
|
94
|
-
|
|
95
|
-
it("logs and ignores a backend when it fails to load and has no cache", async () => {
|
|
96
|
-
fetchMock
|
|
97
|
-
.mockImplementationOnce((url, options) => {
|
|
98
|
-
expect(url).toEqual(
|
|
99
|
-
`http://example.com/compass/${currentUserId}/menu`,
|
|
100
|
-
)
|
|
101
|
-
expect(options.cache).toEqual("default")
|
|
102
|
-
|
|
103
|
-
return Promise.resolve(new Response(null, { status: 404 }))
|
|
104
|
-
})
|
|
105
|
-
.mockImplementationOnce((url, options) => {
|
|
106
|
-
expect(url).toEqual(
|
|
107
|
-
`http://example.com/compass/${currentUserId}/menu`,
|
|
108
|
-
)
|
|
109
|
-
expect(options.cache).toEqual("force-cache")
|
|
110
|
-
|
|
111
|
-
return Promise.resolve(new Response(null, { status: 404 }))
|
|
112
|
-
})
|
|
113
|
-
|
|
114
|
-
const fetchResult = await fetchMenu(
|
|
115
|
-
"http://example.com/compass",
|
|
116
|
-
currentUserId,
|
|
117
|
-
)
|
|
118
|
-
|
|
119
|
-
expect(fetchResult).toEqual({
|
|
120
|
-
items: [],
|
|
121
|
-
error: { message: expect.any(String) },
|
|
122
|
-
})
|
|
123
|
-
})
|
|
124
|
-
})
|
|
125
|
-
|
|
126
|
-
describe("fetchMenus", () => {
|
|
127
|
-
it("merges multiple menu backends", async () => {
|
|
128
|
-
const items1: MenuItem[] = [
|
|
129
|
-
{
|
|
130
|
-
label: "Test Example 1",
|
|
131
|
-
url: "http://example1.com/test1",
|
|
132
|
-
icon: "test-example-1",
|
|
133
|
-
},
|
|
134
|
-
]
|
|
135
|
-
const items2: MenuItem[] = [
|
|
136
|
-
{
|
|
137
|
-
label: "Test Example 2",
|
|
138
|
-
url: "http://example2.com/test2",
|
|
139
|
-
icon: "test-example-2",
|
|
140
|
-
},
|
|
141
|
-
]
|
|
142
|
-
|
|
143
|
-
fetchMock.mockReturnValueOnce(
|
|
144
|
-
Promise.resolve(new Response(JSON.stringify(items1))),
|
|
145
|
-
)
|
|
146
|
-
fetchMock.mockReturnValueOnce(
|
|
147
|
-
Promise.resolve(new Response(JSON.stringify(items2))),
|
|
148
|
-
)
|
|
149
|
-
|
|
150
|
-
const fetchItems = await fetchMenus(
|
|
151
|
-
["http://example1.com/compass/", "http://example2.com/compass/"],
|
|
152
|
-
currentUserId,
|
|
153
|
-
)
|
|
154
|
-
|
|
155
|
-
expect(fetchItems).toEqual([...items1, ...items2])
|
|
156
|
-
})
|
|
157
|
-
|
|
158
|
-
it("filters out errored menu backends", async () => {
|
|
159
|
-
const items1: MenuItem[] = [
|
|
160
|
-
{
|
|
161
|
-
label: "Test Example 1",
|
|
162
|
-
url: "http://example1.com/test1",
|
|
163
|
-
icon: "test-example-1",
|
|
164
|
-
},
|
|
165
|
-
]
|
|
166
|
-
|
|
167
|
-
fetchMock.mockReturnValueOnce(
|
|
168
|
-
Promise.resolve(new Response(JSON.stringify(items1))),
|
|
169
|
-
)
|
|
170
|
-
// Simulate an error result for the second backend
|
|
171
|
-
fetchMock.mockReturnValueOnce(
|
|
172
|
-
Promise.resolve(new Response(null, { status: 404 })),
|
|
173
|
-
)
|
|
174
|
-
|
|
175
|
-
const fetchItems = await fetchMenus(
|
|
176
|
-
["http://example1.com/compass/", "http://example2.com/compass/"],
|
|
177
|
-
currentUserId,
|
|
178
|
-
)
|
|
179
|
-
|
|
180
|
-
expect(fetchItems).toEqual([...items1])
|
|
181
|
-
})
|
|
182
|
-
})
|
|
183
|
-
})
|
package/src/menu/fetchMenu.ts
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { fetchBackend } from "../http"
|
|
2
|
-
import { type MenuItem, type CompassMenuResult } from "./types"
|
|
3
|
-
|
|
4
|
-
export type CompassMenuFilters = {
|
|
5
|
-
tagged?: string
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export async function fetchMenu(
|
|
9
|
-
backend: string,
|
|
10
|
-
contextId: string,
|
|
11
|
-
filters: CompassMenuFilters = {},
|
|
12
|
-
): Promise<CompassMenuResult> {
|
|
13
|
-
try {
|
|
14
|
-
const items = await fetchBackend<MenuItem[]>(
|
|
15
|
-
backend,
|
|
16
|
-
contextId,
|
|
17
|
-
"menu",
|
|
18
|
-
filters,
|
|
19
|
-
)
|
|
20
|
-
return { items }
|
|
21
|
-
} catch (e) {
|
|
22
|
-
return {
|
|
23
|
-
items: [],
|
|
24
|
-
error: { message: e instanceof Error ? e.message : String(e) },
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export async function fetchMenus(
|
|
30
|
-
backends: string[],
|
|
31
|
-
contextId: string,
|
|
32
|
-
filters: CompassMenuFilters = {},
|
|
33
|
-
): Promise<MenuItem[]> {
|
|
34
|
-
const menuPromises = backends.map((backend) =>
|
|
35
|
-
fetchMenu(backend, contextId, filters),
|
|
36
|
-
)
|
|
37
|
-
const results = await Promise.all(menuPromises)
|
|
38
|
-
|
|
39
|
-
return results
|
|
40
|
-
.filter((result) => !result.error)
|
|
41
|
-
.flatMap((result) => result.items)
|
|
42
|
-
}
|
package/src/menu/index.ts
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { type MenuItem } from "./types"
|
|
2
|
-
|
|
3
|
-
export type FlatMenuItem = Omit<MenuItem, "items"> & {
|
|
4
|
-
parent?: FlatMenuItem
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
export type TransformedMenuItem<
|
|
8
|
-
Option extends TransformOptions = TransformOptions,
|
|
9
|
-
> = Option["flatten"] extends true ? FlatMenuItem : MenuItem
|
|
10
|
-
|
|
11
|
-
export const Transformations = {
|
|
12
|
-
flatten(menuItems: MenuItem[], parent?: FlatMenuItem): FlatMenuItem[] {
|
|
13
|
-
return menuItems
|
|
14
|
-
.map(({ items, ...item }) => {
|
|
15
|
-
const parentFlatItem = { parent, ...item }
|
|
16
|
-
|
|
17
|
-
if (items) {
|
|
18
|
-
return this.flatten(items, parentFlatItem)
|
|
19
|
-
} else {
|
|
20
|
-
return parentFlatItem
|
|
21
|
-
}
|
|
22
|
-
})
|
|
23
|
-
.flat()
|
|
24
|
-
},
|
|
25
|
-
|
|
26
|
-
trim(menuItems: MenuItem[]): MenuItem[] {
|
|
27
|
-
return menuItems.filter((item) => item.url || item.items || item.modal)
|
|
28
|
-
},
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export type TransformOption = true | false | undefined
|
|
32
|
-
export type TransformOptionKey = keyof typeof Transformations
|
|
33
|
-
export type TransformOptions = {
|
|
34
|
-
[Property in TransformOptionKey]?: TransformOption
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export function applyTransformations(
|
|
38
|
-
items: MenuItem[],
|
|
39
|
-
options: TransformOptions,
|
|
40
|
-
): TransformedMenuItem[] {
|
|
41
|
-
const optionsUsed = Object.keys(options) as TransformOptionKey[]
|
|
42
|
-
|
|
43
|
-
return optionsUsed.reduce(
|
|
44
|
-
(items: MenuItem[], transformation: TransformOptionKey): MenuItem[] =>
|
|
45
|
-
Transformations[transformation](items),
|
|
46
|
-
items,
|
|
47
|
-
)
|
|
48
|
-
}
|
package/src/menu/types.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { type ComponentType } from "react"
|
|
2
|
-
|
|
3
|
-
export type MenuItem = {
|
|
4
|
-
badge?: number
|
|
5
|
-
gravity?: number
|
|
6
|
-
icon?: string
|
|
7
|
-
items?: MenuItem[]
|
|
8
|
-
label: string
|
|
9
|
-
meta?: Record<string, string>
|
|
10
|
-
tags?: string[]
|
|
11
|
-
url?: string
|
|
12
|
-
modal?: ComponentType
|
|
13
|
-
onSelect?: () => void
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export type CompassMenuResult = {
|
|
17
|
-
items: MenuItem[]
|
|
18
|
-
error?: { message: string }
|
|
19
|
-
}
|