@veiag/payload-cmdk 1.0.0

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.
Files changed (38) hide show
  1. package/README.md +594 -0
  2. package/dist/components/CommandMenuContext.d.ts +15 -0
  3. package/dist/components/CommandMenuContext.js +430 -0
  4. package/dist/components/CommandMenuContext.js.map +1 -0
  5. package/dist/components/SearchButton.d.ts +8 -0
  6. package/dist/components/SearchButton.js +106 -0
  7. package/dist/components/SearchButton.js.map +1 -0
  8. package/dist/components/SearchButton.scss +133 -0
  9. package/dist/components/cmdk/command.scss +334 -0
  10. package/dist/components/cmdk/index.d.ts +12 -0
  11. package/dist/components/cmdk/index.js +77 -0
  12. package/dist/components/cmdk/index.js.map +1 -0
  13. package/dist/components/modal.scss +94 -0
  14. package/dist/endpoints/customEndpointHandler.d.ts +2 -0
  15. package/dist/endpoints/customEndpointHandler.js +7 -0
  16. package/dist/endpoints/customEndpointHandler.js.map +1 -0
  17. package/dist/exports/client.d.ts +2 -0
  18. package/dist/exports/client.js +4 -0
  19. package/dist/exports/client.js.map +1 -0
  20. package/dist/exports/rsc.d.ts +0 -0
  21. package/dist/exports/rsc.js +2 -0
  22. package/dist/exports/rsc.js.map +1 -0
  23. package/dist/hooks/useMutationObserver.d.ts +1 -0
  24. package/dist/hooks/useMutationObserver.js +21 -0
  25. package/dist/hooks/useMutationObserver.js.map +1 -0
  26. package/dist/index.d.ts +3 -0
  27. package/dist/index.js +74 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/translations/index.d.ts +32 -0
  30. package/dist/translations/index.js +38 -0
  31. package/dist/translations/index.js.map +1 -0
  32. package/dist/types.d.ts +223 -0
  33. package/dist/types.js +6 -0
  34. package/dist/types.js.map +1 -0
  35. package/dist/utils/index.d.ts +30 -0
  36. package/dist/utils/index.js +191 -0
  37. package/dist/utils/index.js.map +1 -0
  38. package/package.json +126 -0
@@ -0,0 +1,430 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import './modal.scss';
4
+ import { Modal, useConfig, useModal, useTranslation } from '@payloadcms/ui';
5
+ import { ArrowBigUp, ChevronLeft, Command as CommandIcon, Option } from 'lucide-react';
6
+ import { DynamicIcon } from 'lucide-react/dynamic';
7
+ import { useRouter } from 'next/navigation';
8
+ import { createContext, Fragment, memo, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
9
+ import { useHotkeys } from 'react-hotkeys-hook';
10
+ import { createDefaultGroups } from '../utils/index';
11
+ import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut } from './cmdk/index';
12
+ const MODAL_SLUG = 'command-menu';
13
+ const CommandMenuContext = /*#__PURE__*/ createContext({
14
+ closeMenu: ()=>{},
15
+ currentPage: 'main',
16
+ groups: [],
17
+ isOpen: false,
18
+ items: [],
19
+ openMenu: ()=>{},
20
+ setPage: ()=>{},
21
+ toggleMenu: ()=>{}
22
+ });
23
+ export const useCommandMenu = ()=>{
24
+ const context = useContext(CommandMenuContext);
25
+ return context;
26
+ };
27
+ const ITEM_SELECTOR = `[cmdk-item=""]`;
28
+ function getSelectedElement(containerRef) {
29
+ return containerRef.current?.querySelector(`${ITEM_SELECTOR}[aria-selected="true"]`);
30
+ }
31
+ const CommandMenuComponent = ({ pluginConfig })=>{
32
+ const [search, setSearch] = useState('');
33
+ const [submenuItems, setSubmenuItems] = useState([]);
34
+ const [isLoadingSubmenu, setIsLoadingSubmenu] = useState(false);
35
+ const [isMac, setIsMac] = useState(false);
36
+ const commandListRef = useRef(null);
37
+ const { closeMenu, currentPage, groups, items, setPage } = useCommandMenu();
38
+ const router = useRouter();
39
+ const { t } = useTranslation();
40
+ useEffect(()=>{
41
+ setIsMac(/Mac|iPhone|iPod|iPad/i.test(navigator.platform));
42
+ }, []);
43
+ const submenuEnabled = pluginConfig?.submenu?.enabled !== false;
44
+ const submenuShortcut = pluginConfig?.submenu?.shortcut || 'shift+enter';
45
+ const blurBg = pluginConfig?.blurBg !== false;
46
+ const formatShortcutKey = (key)=>{
47
+ // Handle compound shortcuts like "Shift + Enter"
48
+ const parts = key.split('+').map((part)=>part.trim());
49
+ const elements = parts.map((part, index)=>{
50
+ const lowerPart = part.toLowerCase();
51
+ let content;
52
+ if (lowerPart === 'ctrl' || lowerPart === 'cmd') {
53
+ content = isMac ? /*#__PURE__*/ _jsx(CommandIcon, {
54
+ size: 12
55
+ }) : 'Ctrl';
56
+ } else if (lowerPart === 'meta') {
57
+ content = isMac ? /*#__PURE__*/ _jsx(CommandIcon, {
58
+ size: 12
59
+ }) : 'Ctrl';
60
+ } else if (lowerPart === 'shift') {
61
+ content = isMac ? /*#__PURE__*/ _jsx(ArrowBigUp, {
62
+ size: 12
63
+ }) : 'Shift';
64
+ } else if (lowerPart === 'alt') {
65
+ content = isMac ? /*#__PURE__*/ _jsx(Option, {
66
+ size: 12
67
+ }) : 'Alt';
68
+ } else {
69
+ content = part;
70
+ }
71
+ return /*#__PURE__*/ _jsxs(Fragment, {
72
+ children: [
73
+ content,
74
+ !isMac && index < parts.length - 1 && ' + '
75
+ ]
76
+ }, index);
77
+ });
78
+ return /*#__PURE__*/ _jsx(_Fragment, {
79
+ children: elements
80
+ });
81
+ };
82
+ // Debounced search for submenu
83
+ useEffect(()=>{
84
+ if (currentPage === 'main') {
85
+ return;
86
+ }
87
+ const fetchDocuments = async ()=>{
88
+ if (currentPage.type !== 'collection-search') {
89
+ return;
90
+ }
91
+ setIsLoadingSubmenu(true);
92
+ try {
93
+ const searchParam = search ? `&where[${currentPage.useAsTitle}][like]=${encodeURIComponent(search)}` : '';
94
+ const response = await fetch(`/api/${currentPage.slug}?limit=10${searchParam}&select[${currentPage.useAsTitle}]=true`);
95
+ const data = await response.json();
96
+ if (data.docs && Array.isArray(data.docs)) {
97
+ const docs = data.docs.map((doc)=>({
98
+ slug: `${currentPage.slug}-${doc.id}`,
99
+ type: 'custom',
100
+ action: {
101
+ type: 'link',
102
+ href: `/admin/collections/${currentPage.slug}/${doc.id}`
103
+ },
104
+ icon: pluginConfig?.submenu?.icons?.[currentPage.slug] ?? undefined,
105
+ label: doc[currentPage.useAsTitle] || doc.id
106
+ }));
107
+ setSubmenuItems(docs);
108
+ }
109
+ } catch {
110
+ setSubmenuItems([]);
111
+ } finally{
112
+ setIsLoadingSubmenu(false);
113
+ }
114
+ };
115
+ const timeoutId = setTimeout(fetchDocuments, 300);
116
+ return ()=>clearTimeout(timeoutId);
117
+ }, [
118
+ search,
119
+ currentPage,
120
+ pluginConfig?.submenu?.icons
121
+ ]);
122
+ const handleBack = useCallback(()=>{
123
+ setPage('main');
124
+ setSearch('');
125
+ setSubmenuItems([]);
126
+ }, [
127
+ setPage
128
+ ]);
129
+ const executeItemAction = useCallback(async (item)=>{
130
+ // Execute the item's action
131
+ switch(item.action.type){
132
+ case 'api':
133
+ await fetch(item.action.href, {
134
+ body: item.action.body ? JSON.stringify(item.action.body) : undefined,
135
+ headers: {
136
+ 'Content-Type': 'application/json'
137
+ },
138
+ method: item.action.method || 'GET'
139
+ });
140
+ break;
141
+ case 'link':
142
+ router.push(item.action.href);
143
+ break;
144
+ default:
145
+ break;
146
+ }
147
+ closeMenu();
148
+ setSearch('');
149
+ setPage('main');
150
+ }, [
151
+ router,
152
+ closeMenu,
153
+ setPage
154
+ ]);
155
+ const openSubmenu = useCallback((item)=>{
156
+ setPage({
157
+ slug: item.slug,
158
+ type: 'collection-search',
159
+ label: item.label,
160
+ useAsTitle: item.useAsTitle || 'id',
161
+ useAsTitleLabel: item.useAsTitleLabel || item.useAsTitle || 'id'
162
+ });
163
+ setSearch('');
164
+ //set isLoadingSubmenu to true to show loading state while fetching
165
+ setIsLoadingSubmenu(true);
166
+ setSubmenuItems([]);
167
+ }, [
168
+ setPage
169
+ ]);
170
+ const handleSelect = useCallback(async (item)=>{
171
+ await executeItemAction(item);
172
+ }, [
173
+ executeItemAction
174
+ ]);
175
+ // Handle keyboard events for navigation and back
176
+ useEffect(()=>{
177
+ const handleKeyDown = (e)=>{
178
+ // ESC key for back navigation in submenu
179
+ if (e.key === 'Escape' && currentPage !== 'main') {
180
+ e.preventDefault();
181
+ e.stopPropagation();
182
+ handleBack();
183
+ return;
184
+ }
185
+ // Enter/Shift+Enter handling for collection submenu
186
+ if (e.key === 'Enter' && currentPage === 'main') {
187
+ const selectedElement = getSelectedElement(commandListRef);
188
+ const itemType = selectedElement?.getAttribute('data-item-type');
189
+ const itemSlug = selectedElement?.getAttribute('data-value');
190
+ if (submenuEnabled && itemType === 'collection' && itemSlug) {
191
+ const isShiftPressed = e.shiftKey;
192
+ const shouldOpenSubmenu = submenuShortcut === 'shift+enter' && isShiftPressed || submenuShortcut === 'enter' && !isShiftPressed;
193
+ if (shouldOpenSubmenu) {
194
+ e.preventDefault();
195
+ e.stopPropagation();
196
+ // Find the item in groups
197
+ const item = groups.flatMap((g)=>g.items).find((i)=>i.slug === itemSlug);
198
+ if (item) {
199
+ openSubmenu(item);
200
+ }
201
+ return;
202
+ }
203
+ }
204
+ }
205
+ };
206
+ document.addEventListener('keydown', handleKeyDown, true);
207
+ return ()=>document.removeEventListener('keydown', handleKeyDown, true);
208
+ }, [
209
+ currentPage,
210
+ handleBack,
211
+ submenuEnabled,
212
+ submenuShortcut,
213
+ openSubmenu,
214
+ groups
215
+ ]);
216
+ const placeholder = currentPage === 'main' ? t('cmdkPlugin:search') : t('general:searchBy', {
217
+ label: currentPage.useAsTitleLabel
218
+ });
219
+ const shouldDisableFilter = currentPage !== 'main';
220
+ // Static footer shortcuts based on current page
221
+ const footerShortcuts = currentPage === 'main' && submenuEnabled && submenuShortcut === 'shift+enter' ? [
222
+ {
223
+ key: 'Enter',
224
+ label: t('cmdkPlugin:navigate')
225
+ },
226
+ {
227
+ key: 'Shift + Enter',
228
+ label: t('cmdkPlugin:searchInCollection')
229
+ }
230
+ ] : currentPage === 'main' && submenuEnabled && submenuShortcut === 'enter' ? [
231
+ {
232
+ key: 'Enter',
233
+ label: t('cmdkPlugin:searchInCollection')
234
+ },
235
+ {
236
+ key: 'Shift + Enter',
237
+ label: t('cmdkPlugin:navigate')
238
+ }
239
+ ] : currentPage === 'main' ? [
240
+ {
241
+ key: 'Enter',
242
+ label: t('cmdkPlugin:navigate')
243
+ }
244
+ ] : [
245
+ {
246
+ key: 'Enter',
247
+ label: t('cmdkPlugin:open')
248
+ }
249
+ ];
250
+ const handleBackdropClick = (e)=>{
251
+ // Close modal only if clicking the backdrop (not the command itself)
252
+ if (e.target === e.currentTarget) {
253
+ closeMenu();
254
+ }
255
+ };
256
+ return /*#__PURE__*/ _jsx(Modal, {
257
+ slug: MODAL_SLUG,
258
+ children: /*#__PURE__*/ _jsx("div", {
259
+ className: `command-modal ${blurBg ? 'command-modal--blur' : ''}`,
260
+ onClick: handleBackdropClick,
261
+ children: /*#__PURE__*/ _jsxs(Command, {
262
+ label: "Command Menu",
263
+ shouldFilter: !shouldDisableFilter,
264
+ children: [
265
+ currentPage !== 'main' && /*#__PURE__*/ _jsxs("div", {
266
+ className: "command__header",
267
+ children: [
268
+ /*#__PURE__*/ _jsx("button", {
269
+ className: "command__back-button",
270
+ onClick: handleBack,
271
+ type: "button",
272
+ children: /*#__PURE__*/ _jsx(ChevronLeft, {
273
+ size: 16
274
+ })
275
+ }),
276
+ /*#__PURE__*/ _jsx("span", {
277
+ className: "command__header-label",
278
+ children: t('cmdkPlugin:searchIn', {
279
+ label: currentPage.label
280
+ })
281
+ })
282
+ ]
283
+ }),
284
+ /*#__PURE__*/ _jsx(CommandInput, {
285
+ onValueChange: setSearch,
286
+ placeholder: placeholder,
287
+ value: search
288
+ }),
289
+ /*#__PURE__*/ _jsxs(CommandList, {
290
+ ref: commandListRef,
291
+ children: [
292
+ /*#__PURE__*/ _jsx(CommandEmpty, {
293
+ children: isLoadingSubmenu ? t('cmdkPlugin:loading') : t('cmdkPlugin:noResults')
294
+ }),
295
+ currentPage === 'main' && groups.map((group, index)=>{
296
+ if (group.items.length === 0) {
297
+ return null;
298
+ }
299
+ let titleName = group.title;
300
+ if (group.title === 'Collections') {
301
+ titleName = t('general:collections');
302
+ }
303
+ if (group.title === 'Globals') {
304
+ titleName = t('general:globals');
305
+ }
306
+ const isRenderSeparator = !(index === groups.length - 1 && items.length === 0);
307
+ return /*#__PURE__*/ _jsxs(Fragment, {
308
+ children: [
309
+ /*#__PURE__*/ _jsx(CommandGroup, {
310
+ heading: titleName,
311
+ children: group.items.map((item)=>{
312
+ const isDynamicIcon = typeof item.icon === 'string';
313
+ const IconComponent = isDynamicIcon ? null : item.icon;
314
+ return /*#__PURE__*/ _jsxs(CommandItem, {
315
+ "data-action-type": item.action.type,
316
+ "data-item-type": item.type,
317
+ keywords: [
318
+ group.title
319
+ ],
320
+ onSelect: ()=>handleSelect(item),
321
+ value: item.slug,
322
+ children: [
323
+ isDynamicIcon ? /*#__PURE__*/ _jsx(DynamicIcon, {
324
+ className: "command__item-icon",
325
+ name: item.icon
326
+ }) : IconComponent && /*#__PURE__*/ _jsx(IconComponent, {
327
+ className: "command__item-icon"
328
+ }),
329
+ item.label,
330
+ submenuEnabled && item.type === 'collection' && /*#__PURE__*/ _jsx(CommandShortcut, {
331
+ children: "›"
332
+ })
333
+ ]
334
+ }, item.slug);
335
+ })
336
+ }),
337
+ isRenderSeparator && /*#__PURE__*/ _jsx(CommandSeparator, {})
338
+ ]
339
+ }, group.title);
340
+ }),
341
+ currentPage === 'main' && items?.map((item)=>/*#__PURE__*/ _jsx(CommandItem, {
342
+ "data-action-type": item.action.type,
343
+ "data-item-type": item.type,
344
+ onSelect: ()=>handleSelect(item),
345
+ value: item.slug,
346
+ children: item.label
347
+ }, item.slug)),
348
+ currentPage !== 'main' && submenuItems.map((item)=>{
349
+ const isDynamicIcon = typeof item.icon === 'string';
350
+ const IconComponent = isDynamicIcon ? null : item.icon;
351
+ return /*#__PURE__*/ _jsxs(CommandItem, {
352
+ "data-action-type": item.action.type,
353
+ "data-item-type": item.type,
354
+ onSelect: ()=>handleSelect(item),
355
+ value: item.slug,
356
+ children: [
357
+ isDynamicIcon ? /*#__PURE__*/ _jsx(DynamicIcon, {
358
+ className: "command__item-icon",
359
+ name: item.icon
360
+ }) : IconComponent && /*#__PURE__*/ _jsx(IconComponent, {
361
+ className: "command__item-icon"
362
+ }),
363
+ item.label
364
+ ]
365
+ }, item.slug);
366
+ })
367
+ ]
368
+ }),
369
+ footerShortcuts && footerShortcuts.length > 0 && /*#__PURE__*/ _jsx("div", {
370
+ className: "command__footer",
371
+ children: footerShortcuts.map((shortcut, index)=>/*#__PURE__*/ _jsxs("span", {
372
+ children: [
373
+ /*#__PURE__*/ _jsx("kbd", {
374
+ children: formatShortcutKey(shortcut.key)
375
+ }),
376
+ " ",
377
+ shortcut.label
378
+ ]
379
+ }, index))
380
+ })
381
+ ]
382
+ })
383
+ })
384
+ });
385
+ };
386
+ const MemoizedCommandMenuComponent = /*#__PURE__*/ memo(CommandMenuComponent);
387
+ export const CommandMenuProvider = ({ children, pluginConfig })=>{
388
+ const { closeModal, isModalOpen, openModal, toggleModal } = useModal();
389
+ const [currentPage, setCurrentPage] = useState('main');
390
+ useHotkeys(pluginConfig.shortcut || [
391
+ 'meta+k',
392
+ 'ctrl+k'
393
+ ], (event)=>{
394
+ event.preventDefault();
395
+ event.stopPropagation();
396
+ toggleModal(MODAL_SLUG);
397
+ }, [
398
+ toggleModal
399
+ ]);
400
+ const { config } = useConfig();
401
+ const { i18n } = useTranslation();
402
+ const currentLang = i18n.language;
403
+ const { groups, items } = useMemo(()=>{
404
+ return createDefaultGroups(config, currentLang, pluginConfig);
405
+ }, [
406
+ config,
407
+ currentLang,
408
+ pluginConfig
409
+ ]);
410
+ return /*#__PURE__*/ _jsxs(CommandMenuContext.Provider, {
411
+ value: {
412
+ closeMenu: ()=>closeModal(MODAL_SLUG),
413
+ currentPage,
414
+ groups,
415
+ isOpen: isModalOpen(MODAL_SLUG),
416
+ items,
417
+ openMenu: ()=>openModal(MODAL_SLUG),
418
+ setPage: setCurrentPage,
419
+ toggleMenu: ()=>toggleModal(MODAL_SLUG)
420
+ },
421
+ children: [
422
+ children,
423
+ /*#__PURE__*/ _jsx(MemoizedCommandMenuComponent, {
424
+ pluginConfig: pluginConfig
425
+ })
426
+ ]
427
+ });
428
+ };
429
+
430
+ //# sourceMappingURL=CommandMenuContext.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/CommandMenuContext.tsx"],"sourcesContent":["'use client'\nimport type { LucideIcon } from 'lucide-react'\nimport type { IconName } from 'lucide-react/dynamic'\nimport type { CustomTranslationsKeys, CustomTranslationsObject } from 'src/translations/index'\n\nimport './modal.scss'\n\nimport type {\n CommandMenuContextProps,\n CommandMenuGroup,\n CommandMenuItem,\n CommandMenuPage,\n GenericCollectionDocument,\n PluginCommandMenuConfig,\n} from 'src/types'\n\nimport { Modal, useConfig, useModal, useTranslation } from '@payloadcms/ui'\nimport { ArrowBigUp, ChevronLeft, Command as CommandIcon, Option } from 'lucide-react'\nimport { DynamicIcon } from 'lucide-react/dynamic'\nimport { useRouter } from 'next/navigation'\nimport {\n createContext,\n Fragment,\n memo,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react'\nimport { useHotkeys } from 'react-hotkeys-hook'\n\nimport { createDefaultGroups } from '../utils/index'\nimport {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n CommandSeparator,\n CommandShortcut,\n} from './cmdk/index'\n\nconst MODAL_SLUG = 'command-menu'\n\ninterface CommandMenuContextType {\n closeMenu: () => void\n currentPage: CommandMenuPage\n groups: CommandMenuGroup[]\n isOpen: boolean\n items: CommandMenuItem[]\n openMenu: () => void\n setPage: (page: CommandMenuPage) => void\n toggleMenu: () => void\n}\n\nconst CommandMenuContext = createContext<CommandMenuContextType>({\n closeMenu: () => {},\n currentPage: 'main',\n groups: [],\n isOpen: false,\n items: [],\n openMenu: () => {},\n setPage: () => {},\n toggleMenu: () => {},\n})\n\nexport const useCommandMenu = () => {\n const context = useContext(CommandMenuContext)\n return context\n}\n\nconst ITEM_SELECTOR = `[cmdk-item=\"\"]`\n\nfunction getSelectedElement(containerRef: React.RefObject<HTMLElement | null>) {\n return containerRef.current?.querySelector(`${ITEM_SELECTOR}[aria-selected=\"true\"]`)\n}\n\nconst CommandMenuComponent: React.FC<{\n pluginConfig: PluginCommandMenuConfig\n}> = ({ pluginConfig }) => {\n const [search, setSearch] = useState('')\n const [submenuItems, setSubmenuItems] = useState<CommandMenuItem[]>([])\n const [isLoadingSubmenu, setIsLoadingSubmenu] = useState(false)\n const [isMac, setIsMac] = useState(false)\n\n const commandListRef = useRef<HTMLDivElement>(null)\n\n const { closeMenu, currentPage, groups, items, setPage } = useCommandMenu()\n const router = useRouter()\n const { t } = useTranslation<CustomTranslationsObject, CustomTranslationsKeys>()\n\n useEffect(() => {\n setIsMac(/Mac|iPhone|iPod|iPad/i.test(navigator.platform))\n }, [])\n\n const submenuEnabled = pluginConfig?.submenu?.enabled !== false\n const submenuShortcut = pluginConfig?.submenu?.shortcut || 'shift+enter'\n const blurBg = pluginConfig?.blurBg !== false\n\n const formatShortcutKey = (key: string): React.ReactNode => {\n // Handle compound shortcuts like \"Shift + Enter\"\n const parts = key.split('+').map((part) => part.trim())\n const elements = parts.map((part, index) => {\n const lowerPart = part.toLowerCase()\n let content: React.ReactNode\n\n if (lowerPart === 'ctrl' || lowerPart === 'cmd') {\n content = isMac ? <CommandIcon size={12} /> : 'Ctrl'\n } else if (lowerPart === 'meta') {\n content = isMac ? <CommandIcon size={12} /> : 'Ctrl'\n } else if (lowerPart === 'shift') {\n content = isMac ? <ArrowBigUp size={12} /> : 'Shift'\n } else if (lowerPart === 'alt') {\n content = isMac ? <Option size={12} /> : 'Alt'\n } else {\n content = part\n }\n\n return (\n <Fragment key={index}>\n {content}\n {!isMac && index < parts.length - 1 && ' + '}\n </Fragment>\n )\n })\n\n return <>{elements}</>\n }\n\n // Debounced search for submenu\n useEffect(() => {\n if (currentPage === 'main') {\n return\n }\n\n const fetchDocuments = async () => {\n if (currentPage.type !== 'collection-search') {\n return\n }\n\n setIsLoadingSubmenu(true)\n try {\n const searchParam = search\n ? `&where[${currentPage.useAsTitle}][like]=${encodeURIComponent(search)}`\n : ''\n const response = await fetch(\n `/api/${currentPage.slug}?limit=10${searchParam}&select[${currentPage.useAsTitle}]=true`,\n )\n const data = await response.json()\n\n if (data.docs && Array.isArray(data.docs)) {\n const docs: CommandMenuItem[] = data.docs.map((doc: GenericCollectionDocument) => ({\n slug: `${currentPage.slug}-${doc.id}`,\n type: 'custom' as const,\n action: {\n type: 'link',\n href: `/admin/collections/${currentPage.slug}/${doc.id}`,\n },\n icon: pluginConfig?.submenu?.icons?.[currentPage.slug] ?? undefined,\n label: doc[currentPage.useAsTitle] || doc.id,\n }))\n setSubmenuItems(docs)\n }\n } catch {\n setSubmenuItems([])\n } finally {\n setIsLoadingSubmenu(false)\n }\n }\n\n const timeoutId = setTimeout(fetchDocuments, 300)\n return () => clearTimeout(timeoutId)\n }, [search, currentPage, pluginConfig?.submenu?.icons])\n\n const handleBack = useCallback(() => {\n setPage('main')\n setSearch('')\n setSubmenuItems([])\n }, [setPage])\n\n const executeItemAction = useCallback(\n async (item: CommandMenuItem) => {\n // Execute the item's action\n switch (item.action.type) {\n case 'api':\n await fetch(item.action.href, {\n body: item.action.body ? JSON.stringify(item.action.body) : undefined,\n headers: {\n 'Content-Type': 'application/json',\n },\n method: item.action.method || 'GET',\n })\n break\n case 'link':\n router.push(item.action.href)\n break\n default:\n break\n }\n closeMenu()\n setSearch('')\n setPage('main')\n },\n [router, closeMenu, setPage],\n )\n\n const openSubmenu = useCallback(\n (item: CommandMenuItem) => {\n setPage({\n slug: item.slug,\n type: 'collection-search',\n label: item.label,\n useAsTitle: item.useAsTitle || 'id',\n useAsTitleLabel: item.useAsTitleLabel || item.useAsTitle || 'id',\n })\n setSearch('')\n //set isLoadingSubmenu to true to show loading state while fetching\n setIsLoadingSubmenu(true)\n setSubmenuItems([])\n },\n [setPage],\n )\n\n const handleSelect = useCallback(\n async (item: CommandMenuItem) => {\n await executeItemAction(item)\n },\n [executeItemAction],\n )\n\n // Handle keyboard events for navigation and back\n useEffect(() => {\n const handleKeyDown = (e: KeyboardEvent) => {\n // ESC key for back navigation in submenu\n if (e.key === 'Escape' && currentPage !== 'main') {\n e.preventDefault()\n e.stopPropagation()\n handleBack()\n return\n }\n\n // Enter/Shift+Enter handling for collection submenu\n if (e.key === 'Enter' && currentPage === 'main') {\n const selectedElement = getSelectedElement(commandListRef)\n const itemType = selectedElement?.getAttribute('data-item-type')\n const itemSlug = selectedElement?.getAttribute('data-value')\n\n if (submenuEnabled && itemType === 'collection' && itemSlug) {\n const isShiftPressed = e.shiftKey\n const shouldOpenSubmenu =\n (submenuShortcut === 'shift+enter' && isShiftPressed) ||\n (submenuShortcut === 'enter' && !isShiftPressed)\n\n if (shouldOpenSubmenu) {\n e.preventDefault()\n e.stopPropagation()\n\n // Find the item in groups\n const item = groups.flatMap((g) => g.items).find((i) => i.slug === itemSlug)\n if (item) {\n openSubmenu(item)\n }\n return\n }\n }\n }\n }\n\n document.addEventListener('keydown', handleKeyDown, true)\n return () => document.removeEventListener('keydown', handleKeyDown, true)\n }, [currentPage, handleBack, submenuEnabled, submenuShortcut, openSubmenu, groups])\n\n const placeholder =\n currentPage === 'main'\n ? t('cmdkPlugin:search')\n : t('general:searchBy', {\n label: currentPage.useAsTitleLabel,\n })\n\n const shouldDisableFilter = currentPage !== 'main'\n\n // Static footer shortcuts based on current page\n const footerShortcuts = currentPage === 'main' && submenuEnabled && submenuShortcut === 'shift+enter'\n ? [\n { key: 'Enter', label: t('cmdkPlugin:navigate') },\n { key: 'Shift + Enter', label: t('cmdkPlugin:searchInCollection') },\n ]\n : currentPage === 'main' && submenuEnabled && submenuShortcut === 'enter'\n ? [\n { key: 'Enter', label: t('cmdkPlugin:searchInCollection') },\n { key: 'Shift + Enter', label: t('cmdkPlugin:navigate') },\n ]\n : currentPage === 'main'\n ? [{ key: 'Enter', label: t('cmdkPlugin:navigate') }]\n : [{ key: 'Enter', label: t('cmdkPlugin:open') }]\n\n const handleBackdropClick = (e: React.MouseEvent<HTMLDivElement>) => {\n // Close modal only if clicking the backdrop (not the command itself)\n if (e.target === e.currentTarget) {\n closeMenu()\n }\n }\n\n return (\n <Modal slug={MODAL_SLUG}>\n {/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */}\n <div\n className={`command-modal ${blurBg ? 'command-modal--blur' : ''}`}\n onClick={handleBackdropClick}\n >\n <Command label=\"Command Menu\" shouldFilter={!shouldDisableFilter}>\n {/* Header for submenu navigation */}\n {currentPage !== 'main' && (\n <div className=\"command__header\">\n <button className=\"command__back-button\" onClick={handleBack} type=\"button\">\n <ChevronLeft size={16} />\n </button>\n <span className=\"command__header-label\">\n {t('cmdkPlugin:searchIn', { label: currentPage.label })}\n </span>\n </div>\n )}\n\n <CommandInput onValueChange={setSearch} placeholder={placeholder} value={search} />\n <CommandList ref={commandListRef}>\n <CommandEmpty>\n {isLoadingSubmenu ? t('cmdkPlugin:loading') : t('cmdkPlugin:noResults')}\n </CommandEmpty>\n\n {/* Main page view */}\n {currentPage === 'main' &&\n groups.map((group, index) => {\n if (group.items.length === 0) {\n return null\n }\n\n let titleName = group.title\n if (group.title === 'Collections') {\n titleName = t('general:collections')\n }\n if (group.title === 'Globals') {\n titleName = t('general:globals')\n }\n\n const isRenderSeparator = !(index === groups.length - 1 && items.length === 0)\n return (\n <Fragment key={group.title}>\n <CommandGroup heading={titleName}>\n {group.items.map((item) => {\n const isDynamicIcon = typeof item.icon === 'string'\n const IconComponent = isDynamicIcon ? null : (item.icon as LucideIcon)\n return (\n <CommandItem\n data-action-type={item.action.type}\n data-item-type={item.type}\n key={item.slug}\n keywords={[group.title]}\n onSelect={() => handleSelect(item)}\n value={item.slug}\n >\n {isDynamicIcon ? (\n <DynamicIcon\n className=\"command__item-icon\"\n name={item.icon as IconName}\n />\n ) : (\n IconComponent && <IconComponent className=\"command__item-icon\" />\n )}\n {item.label}\n {submenuEnabled && item.type === 'collection' && (\n <CommandShortcut>›</CommandShortcut>\n )}\n </CommandItem>\n )\n })}\n </CommandGroup>\n {isRenderSeparator && <CommandSeparator />}\n </Fragment>\n )\n })}\n\n {/* Stray items on main page */}\n {currentPage === 'main' &&\n items?.map((item) => (\n <CommandItem\n data-action-type={item.action.type}\n data-item-type={item.type}\n key={item.slug}\n onSelect={() => handleSelect(item)}\n value={item.slug}\n >\n {item.label}\n </CommandItem>\n ))}\n\n {/* Submenu page view */}\n {currentPage !== 'main' &&\n submenuItems.map((item) => {\n const isDynamicIcon = typeof item.icon === 'string'\n const IconComponent = isDynamicIcon ? null : (item.icon as LucideIcon)\n return (\n <CommandItem\n data-action-type={item.action.type}\n data-item-type={item.type}\n key={item.slug}\n onSelect={() => handleSelect(item)}\n value={item.slug}\n >\n {isDynamicIcon ? (\n <DynamicIcon className=\"command__item-icon\" name={item.icon as IconName} />\n ) : (\n IconComponent && <IconComponent className=\"command__item-icon\" />\n )}\n {item.label}\n </CommandItem>\n )\n })}\n </CommandList>\n\n {/* Footer with static shortcuts */}\n {footerShortcuts && footerShortcuts.length > 0 && (\n <div className=\"command__footer\">\n {footerShortcuts.map((shortcut, index) => (\n <span key={index}>\n <kbd>{formatShortcutKey(shortcut.key)}</kbd> {shortcut.label}\n </span>\n ))}\n </div>\n )}\n </Command>\n </div>\n </Modal>\n )\n}\n\nconst MemoizedCommandMenuComponent = memo(CommandMenuComponent)\n\nexport const CommandMenuProvider: React.FC<CommandMenuContextProps> = ({\n children,\n pluginConfig,\n}) => {\n const { closeModal, isModalOpen, openModal, toggleModal } = useModal()\n const [currentPage, setCurrentPage] = useState<CommandMenuPage>('main')\n\n useHotkeys(\n pluginConfig.shortcut || ['meta+k', 'ctrl+k'],\n (event) => {\n event.preventDefault()\n event.stopPropagation()\n toggleModal(MODAL_SLUG)\n },\n [toggleModal],\n )\n const { config } = useConfig()\n const { i18n } = useTranslation()\n const currentLang = i18n.language\n const { groups, items } = useMemo(() => {\n return createDefaultGroups(config, currentLang, pluginConfig)\n }, [config, currentLang, pluginConfig])\n\n return (\n <CommandMenuContext.Provider\n value={{\n closeMenu: () => closeModal(MODAL_SLUG),\n currentPage,\n groups,\n isOpen: isModalOpen(MODAL_SLUG),\n items,\n openMenu: () => openModal(MODAL_SLUG),\n setPage: setCurrentPage,\n toggleMenu: () => toggleModal(MODAL_SLUG),\n }}\n >\n {children}\n <MemoizedCommandMenuComponent pluginConfig={pluginConfig} />\n </CommandMenuContext.Provider>\n )\n}\n"],"names":["Modal","useConfig","useModal","useTranslation","ArrowBigUp","ChevronLeft","Command","CommandIcon","Option","DynamicIcon","useRouter","createContext","Fragment","memo","useCallback","useContext","useEffect","useMemo","useRef","useState","useHotkeys","createDefaultGroups","CommandEmpty","CommandGroup","CommandInput","CommandItem","CommandList","CommandSeparator","CommandShortcut","MODAL_SLUG","CommandMenuContext","closeMenu","currentPage","groups","isOpen","items","openMenu","setPage","toggleMenu","useCommandMenu","context","ITEM_SELECTOR","getSelectedElement","containerRef","current","querySelector","CommandMenuComponent","pluginConfig","search","setSearch","submenuItems","setSubmenuItems","isLoadingSubmenu","setIsLoadingSubmenu","isMac","setIsMac","commandListRef","router","t","test","navigator","platform","submenuEnabled","submenu","enabled","submenuShortcut","shortcut","blurBg","formatShortcutKey","key","parts","split","map","part","trim","elements","index","lowerPart","toLowerCase","content","size","length","fetchDocuments","type","searchParam","useAsTitle","encodeURIComponent","response","fetch","slug","data","json","docs","Array","isArray","doc","id","action","href","icon","icons","undefined","label","timeoutId","setTimeout","clearTimeout","handleBack","executeItemAction","item","body","JSON","stringify","headers","method","push","openSubmenu","useAsTitleLabel","handleSelect","handleKeyDown","e","preventDefault","stopPropagation","selectedElement","itemType","getAttribute","itemSlug","isShiftPressed","shiftKey","shouldOpenSubmenu","flatMap","g","find","i","document","addEventListener","removeEventListener","placeholder","shouldDisableFilter","footerShortcuts","handleBackdropClick","target","currentTarget","div","className","onClick","shouldFilter","button","span","onValueChange","value","ref","group","titleName","title","isRenderSeparator","heading","isDynamicIcon","IconComponent","data-action-type","data-item-type","keywords","onSelect","name","kbd","MemoizedCommandMenuComponent","CommandMenuProvider","children","closeModal","isModalOpen","openModal","toggleModal","setCurrentPage","event","config","i18n","currentLang","language","Provider"],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import './SearchButton.scss';
2
+ import React from 'react';
3
+ interface SearchButtonProps {
4
+ position?: 'actions' | 'nav';
5
+ shortcut?: string | string[];
6
+ }
7
+ declare const SearchButton: React.FC<SearchButtonProps>;
8
+ export default SearchButton;
@@ -0,0 +1,106 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import './SearchButton.scss';
4
+ import { useTranslation } from '@payloadcms/ui';
5
+ import { ArrowBigUp, Command as CommandIcon, Option, SearchIcon } from 'lucide-react';
6
+ import React, { useEffect, useState } from 'react';
7
+ import { useCommandMenu } from './CommandMenuContext';
8
+ const SearchButton = ({ position = 'actions', shortcut = [
9
+ 'meta+k',
10
+ 'ctrl+k'
11
+ ] })=>{
12
+ const { openMenu } = useCommandMenu();
13
+ const { t } = useTranslation();
14
+ const [isMac, setIsMac] = useState(false);
15
+ useEffect(()=>{
16
+ setIsMac(/Mac|iPhone|iPod|iPad/i.test(navigator.platform));
17
+ }, []);
18
+ const formatShortcut = (shortcutString)=>{
19
+ const parts = shortcutString.split('+').map((part)=>part.trim().toLowerCase());
20
+ return parts.map((part)=>{
21
+ if (part === 'ctrl' || part === 'cmd') {
22
+ return isMac ? {
23
+ icon: /*#__PURE__*/ _jsx(CommandIcon, {
24
+ size: 12
25
+ })
26
+ } : {
27
+ text: 'Ctrl'
28
+ };
29
+ }
30
+ if (part === 'meta') {
31
+ return isMac ? {
32
+ icon: /*#__PURE__*/ _jsx(CommandIcon, {
33
+ size: 12
34
+ })
35
+ } : {
36
+ text: 'Ctrl'
37
+ };
38
+ }
39
+ if (part === 'shift') {
40
+ return isMac ? {
41
+ icon: /*#__PURE__*/ _jsx(ArrowBigUp, {
42
+ size: 12
43
+ })
44
+ } : {
45
+ text: 'Shift'
46
+ };
47
+ }
48
+ if (part === 'alt') {
49
+ return isMac ? {
50
+ icon: /*#__PURE__*/ _jsx(Option, {
51
+ size: 12
52
+ })
53
+ } : {
54
+ text: 'Alt'
55
+ };
56
+ }
57
+ return {
58
+ text: part.toUpperCase()
59
+ };
60
+ });
61
+ };
62
+ // Select the appropriate shortcut to display based on platform
63
+ const getDisplayShortcut = ()=>{
64
+ if (typeof shortcut === 'string') {
65
+ return shortcut;
66
+ }
67
+ // If array, prefer meta shortcut for Mac, ctrl shortcut for others
68
+ const metaShortcut = shortcut.find((s)=>s.toLowerCase().includes('meta'));
69
+ const ctrlShortcut = shortcut.find((s)=>s.toLowerCase().includes('ctrl'));
70
+ if (isMac && metaShortcut) {
71
+ return metaShortcut;
72
+ }
73
+ if (!isMac && ctrlShortcut) {
74
+ return ctrlShortcut;
75
+ }
76
+ // Fallback to first shortcut
77
+ return shortcut[0] || 'meta+k';
78
+ };
79
+ const shortcutParts = formatShortcut(getDisplayShortcut());
80
+ return /*#__PURE__*/ _jsx("button", {
81
+ className: `search-button ${position === 'nav' ? 'search-button--nav' : 'search-button--actions'}`,
82
+ onClick: openMenu,
83
+ type: "button",
84
+ children: /*#__PURE__*/ _jsxs("div", {
85
+ className: "search-button__wrapper",
86
+ children: [
87
+ /*#__PURE__*/ _jsx(SearchIcon, {
88
+ className: "search-button__icon"
89
+ }),
90
+ position === 'actions' && /*#__PURE__*/ _jsx("span", {
91
+ className: "search-button__placeholder",
92
+ children: t('cmdkPlugin:searchShort')
93
+ }),
94
+ /*#__PURE__*/ _jsx("div", {
95
+ className: "search-button__shortcuts",
96
+ children: shortcutParts.map((part, index)=>/*#__PURE__*/ _jsx("kbd", {
97
+ children: part.icon ? part.icon : part.text
98
+ }, index))
99
+ })
100
+ ]
101
+ })
102
+ });
103
+ };
104
+ export default SearchButton;
105
+
106
+ //# sourceMappingURL=SearchButton.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/SearchButton.tsx"],"sourcesContent":["'use client'\n\nimport './SearchButton.scss'\n\nimport { useTranslation } from '@payloadcms/ui'\nimport { ArrowBigUp, Command as CommandIcon, Option, SearchIcon } from 'lucide-react'\nimport React, { useEffect, useState } from 'react'\n\nimport type { CustomTranslationsKeys, CustomTranslationsObject } from '../translations'\n\nimport { useCommandMenu } from './CommandMenuContext'\n\ninterface SearchButtonProps {\n position?: 'actions' | 'nav'\n shortcut?: string | string[]\n}\n\nconst SearchButton: React.FC<SearchButtonProps> = ({\n position = 'actions',\n shortcut = ['meta+k', 'ctrl+k'],\n}) => {\n const { openMenu } = useCommandMenu()\n const { t } = useTranslation<CustomTranslationsObject, CustomTranslationsKeys>()\n const [isMac, setIsMac] = useState(false)\n\n useEffect(() => {\n setIsMac(/Mac|iPhone|iPod|iPad/i.test(navigator.platform))\n }, [])\n\n const formatShortcut = (\n shortcutString: string,\n ): Array<{ icon?: React.ReactNode; text?: string }> => {\n const parts = shortcutString.split('+').map((part) => part.trim().toLowerCase())\n return parts.map((part) => {\n if (part === 'ctrl' || part === 'cmd') {\n return isMac ? { icon: <CommandIcon size={12} /> } : { text: 'Ctrl' }\n }\n if (part === 'meta') {\n return isMac ? { icon: <CommandIcon size={12} /> } : { text: 'Ctrl' }\n }\n if (part === 'shift') {\n return isMac ? { icon: <ArrowBigUp size={12} /> } : { text: 'Shift' }\n }\n if (part === 'alt') {\n return isMac ? { icon: <Option size={12} /> } : { text: 'Alt' }\n }\n return { text: part.toUpperCase() }\n })\n }\n\n // Select the appropriate shortcut to display based on platform\n const getDisplayShortcut = (): string => {\n if (typeof shortcut === 'string') {\n return shortcut\n }\n // If array, prefer meta shortcut for Mac, ctrl shortcut for others\n const metaShortcut = shortcut.find((s) => s.toLowerCase().includes('meta'))\n const ctrlShortcut = shortcut.find((s) => s.toLowerCase().includes('ctrl'))\n\n if (isMac && metaShortcut) {\n return metaShortcut\n }\n if (!isMac && ctrlShortcut) {\n return ctrlShortcut\n }\n // Fallback to first shortcut\n return shortcut[0] || 'meta+k'\n }\n\n const shortcutParts = formatShortcut(getDisplayShortcut())\n\n return (\n <button\n className={`search-button ${position === 'nav' ? 'search-button--nav' : 'search-button--actions'}`}\n onClick={openMenu}\n type=\"button\"\n >\n <div className=\"search-button__wrapper\">\n <SearchIcon className=\"search-button__icon\" />\n {position === 'actions' && (\n <span className=\"search-button__placeholder\">{t('cmdkPlugin:searchShort')}</span>\n )}\n <div className=\"search-button__shortcuts\">\n {shortcutParts.map((part, index) => (\n <kbd key={index}>{part.icon ? part.icon : part.text}</kbd>\n ))}\n </div>\n </div>\n </button>\n )\n}\n\nexport default SearchButton\n"],"names":["useTranslation","ArrowBigUp","Command","CommandIcon","Option","SearchIcon","React","useEffect","useState","useCommandMenu","SearchButton","position","shortcut","openMenu","t","isMac","setIsMac","test","navigator","platform","formatShortcut","shortcutString","parts","split","map","part","trim","toLowerCase","icon","size","text","toUpperCase","getDisplayShortcut","metaShortcut","find","s","includes","ctrlShortcut","shortcutParts","button","className","onClick","type","div","span","index","kbd"],"mappings":"AAAA;;AAEA,OAAO,sBAAqB;AAE5B,SAASA,cAAc,QAAQ,iBAAgB;AAC/C,SAASC,UAAU,EAAEC,WAAWC,WAAW,EAAEC,MAAM,EAAEC,UAAU,QAAQ,eAAc;AACrF,OAAOC,SAASC,SAAS,EAAEC,QAAQ,QAAQ,QAAO;AAIlD,SAASC,cAAc,QAAQ,uBAAsB;AAOrD,MAAMC,eAA4C,CAAC,EACjDC,WAAW,SAAS,EACpBC,WAAW;IAAC;IAAU;CAAS,EAChC;IACC,MAAM,EAAEC,QAAQ,EAAE,GAAGJ;IACrB,MAAM,EAAEK,CAAC,EAAE,GAAGd;IACd,MAAM,CAACe,OAAOC,SAAS,GAAGR,SAAS;IAEnCD,UAAU;QACRS,SAAS,wBAAwBC,IAAI,CAACC,UAAUC,QAAQ;IAC1D,GAAG,EAAE;IAEL,MAAMC,iBAAiB,CACrBC;QAEA,MAAMC,QAAQD,eAAeE,KAAK,CAAC,KAAKC,GAAG,CAAC,CAACC,OAASA,KAAKC,IAAI,GAAGC,WAAW;QAC7E,OAAOL,MAAME,GAAG,CAAC,CAACC;YAChB,IAAIA,SAAS,UAAUA,SAAS,OAAO;gBACrC,OAAOV,QAAQ;oBAAEa,oBAAM,KAACzB;wBAAY0B,MAAM;;gBAAO,IAAI;oBAAEC,MAAM;gBAAO;YACtE;YACA,IAAIL,SAAS,QAAQ;gBACnB,OAAOV,QAAQ;oBAAEa,oBAAM,KAACzB;wBAAY0B,MAAM;;gBAAO,IAAI;oBAAEC,MAAM;gBAAO;YACtE;YACA,IAAIL,SAAS,SAAS;gBACpB,OAAOV,QAAQ;oBAAEa,oBAAM,KAAC3B;wBAAW4B,MAAM;;gBAAO,IAAI;oBAAEC,MAAM;gBAAQ;YACtE;YACA,IAAIL,SAAS,OAAO;gBAClB,OAAOV,QAAQ;oBAAEa,oBAAM,KAACxB;wBAAOyB,MAAM;;gBAAO,IAAI;oBAAEC,MAAM;gBAAM;YAChE;YACA,OAAO;gBAAEA,MAAML,KAAKM,WAAW;YAAG;QACpC;IACF;IAEA,+DAA+D;IAC/D,MAAMC,qBAAqB;QACzB,IAAI,OAAOpB,aAAa,UAAU;YAChC,OAAOA;QACT;QACA,mEAAmE;QACnE,MAAMqB,eAAerB,SAASsB,IAAI,CAAC,CAACC,IAAMA,EAAER,WAAW,GAAGS,QAAQ,CAAC;QACnE,MAAMC,eAAezB,SAASsB,IAAI,CAAC,CAACC,IAAMA,EAAER,WAAW,GAAGS,QAAQ,CAAC;QAEnE,IAAIrB,SAASkB,cAAc;YACzB,OAAOA;QACT;QACA,IAAI,CAAClB,SAASsB,cAAc;YAC1B,OAAOA;QACT;QACA,6BAA6B;QAC7B,OAAOzB,QAAQ,CAAC,EAAE,IAAI;IACxB;IAEA,MAAM0B,gBAAgBlB,eAAeY;IAErC,qBACE,KAACO;QACCC,WAAW,CAAC,cAAc,EAAE7B,aAAa,QAAQ,uBAAuB,0BAA0B;QAClG8B,SAAS5B;QACT6B,MAAK;kBAEL,cAAA,MAACC;YAAIH,WAAU;;8BACb,KAACnC;oBAAWmC,WAAU;;gBACrB7B,aAAa,2BACZ,KAACiC;oBAAKJ,WAAU;8BAA8B1B,EAAE;;8BAElD,KAAC6B;oBAAIH,WAAU;8BACZF,cAAcd,GAAG,CAAC,CAACC,MAAMoB,sBACxB,KAACC;sCAAiBrB,KAAKG,IAAI,GAAGH,KAAKG,IAAI,GAAGH,KAAKK,IAAI;2BAAzCe;;;;;AAMtB;AAEA,eAAenC,aAAY"}