betterstart-cli 0.0.12 → 0.0.14
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/assets/adapters/next/plugins/blog/plugin.ts +1 -1
- package/dist/assets/adapters/next/plugins/blog/schemas/menus.json +155 -0
- package/dist/assets/adapters/next/plugins/blog/schemas/posts.json +2 -7
- package/dist/assets/adapters/next/templates/init/components/layouts/admin-sidebar.tsx +9 -3
- package/dist/assets/adapters/next/templates/init/components/layouts/sidebar-branding.tsx +1 -1
- package/dist/assets/adapters/next/templates/init/components/shared/data-table/data-grid.tsx +107 -0
- package/dist/assets/adapters/next/templates/init/components/shared/data-table/data-table.tsx +58 -45
- package/dist/assets/adapters/next/templates/init/components/shared/dev-mode/lifecycle-hooks-tab.tsx +1 -1
- package/dist/assets/adapters/next/templates/init/components/shared/dev-mode-integrate.tsx +1 -1
- package/dist/assets/adapters/next/templates/init/components/shared/entity-filters-bar.tsx +1 -1
- package/dist/assets/adapters/next/templates/init/components/shared/entity-versions/entity-versions-drawer.tsx +1 -1
- package/dist/assets/adapters/next/templates/init/components/shared/page-header.tsx +20 -17
- package/dist/assets/adapters/next/templates/init/components/shared/sort-indicator.tsx +7 -3
- package/dist/assets/adapters/next/templates/init/data/navigation.ts +4 -1
- package/dist/assets/adapters/next/templates/init/hooks/content-editor/use-content-editor-slash-menu.ts +28 -2
- package/dist/assets/adapters/next/templates/init/hooks/content-editor/use-content-editor.ts +22 -1
- package/dist/assets/adapters/next/templates/init/hooks/content-editor/use-table-button.ts +85 -0
- package/dist/assets/adapters/next/templates/init/hooks/use-audit-log.ts +23 -0
- package/dist/assets/adapters/next/templates/init/lib/actions/audit/cleanup-audit-logs.ts +63 -0
- package/dist/assets/adapters/next/templates/init/lib/actions/audit/get-audit-logs.ts +122 -0
- package/dist/assets/adapters/next/templates/init/lib/actions/audit/index.ts +19 -0
- package/dist/assets/adapters/next/templates/init/lib/actions/audit/record-audit-event.ts +37 -0
- package/dist/assets/adapters/next/templates/init/lib/actions/audit/register.ts +13 -0
- package/dist/assets/adapters/next/templates/init/lib/actions/audit/request-metadata.ts +37 -0
- package/dist/assets/adapters/next/templates/init/lib/actions/audit/types.ts +77 -0
- package/dist/assets/adapters/next/templates/init/lib/actions/forms/upsert-form-settings.ts +3 -0
- package/dist/assets/adapters/next/templates/init/lib/actions/media/create-media.ts +19 -0
- package/dist/assets/adapters/next/templates/init/lib/actions/media/delete-media-bulk.ts +23 -0
- package/dist/assets/adapters/next/templates/init/lib/actions/media/delete-media.ts +21 -0
- package/dist/assets/adapters/next/templates/init/lib/actions/media/update-media.ts +23 -1
- package/dist/assets/adapters/next/templates/init/lib/actions/profile/update-email.ts +17 -0
- package/dist/assets/adapters/next/templates/init/lib/actions/upload/upload-files.ts +3 -0
- package/dist/assets/adapters/next/templates/init/lib/actions/upload/upload-media-from-url.ts +3 -0
- package/dist/assets/adapters/next/templates/init/lib/actions/users/create-user.ts +19 -1
- package/dist/assets/adapters/next/templates/init/lib/actions/users/delete-user.ts +23 -1
- package/dist/assets/adapters/next/templates/init/lib/actions/users/update-user-role.ts +23 -1
- package/dist/assets/adapters/next/templates/init/lib/db/core/schema.ts +49 -0
- package/dist/assets/adapters/next/templates/init/lib/lifecycle-hooks/index.ts +6 -1
- package/dist/assets/adapters/next/templates/init/lib/lifecycle-hooks/register.ts +3 -0
- package/dist/assets/adapters/next/templates/init/lib/lifecycle-hooks/registry.ts +36 -1
- package/dist/assets/adapters/next/templates/init/lib/lifecycle-hooks/runner.ts +117 -12
- package/dist/assets/adapters/next/templates/init/lib/lifecycle-hooks/types.ts +35 -1
- package/dist/assets/adapters/next/templates/init/pages/audit-log/audit-log-detail-drawer.tsx +104 -0
- package/dist/assets/adapters/next/templates/init/pages/audit-log/audit-log-detail-row.tsx +15 -0
- package/dist/assets/adapters/next/templates/init/pages/audit-log/audit-log-page-content.tsx +565 -0
- package/dist/assets/adapters/next/templates/init/pages/audit-log/audit-log-page-skeleton.tsx +7 -0
- package/dist/assets/adapters/next/templates/init/pages/audit-log/audit-log-page.tsx +14 -0
- package/dist/assets/adapters/next/templates/init/pages/users/users-page-content.tsx +83 -2
- package/dist/assets/adapters/next/templates/init/pages/users/users-table.tsx +126 -78
- package/dist/assets/adapters/next/templates/init/utils/audit/audit.ts +54 -0
- package/dist/assets/adapters/next/templates/init/utils/editor/tiptap.ts +7 -32
- package/dist/assets/adapters/next/templates/init/utils/form/list-field.ts +17 -0
- package/dist/assets/adapters/next/templates/init/utils/user/user.ts +70 -0
- package/dist/assets/shared-assets/react-admin/custom/content-editor/horizontal-rule-button.tsx +1 -1
- package/dist/assets/shared-assets/react-admin/custom/content-editor/index.tsx +6 -0
- package/dist/assets/shared-assets/react-admin/custom/content-editor/main-toolbar-content.tsx +1 -1
- package/dist/assets/shared-assets/react-admin/custom/content-editor/math-button.tsx +1 -1
- package/dist/assets/shared-assets/react-admin/custom/content-editor/math-editor-controls.tsx +27 -40
- package/dist/assets/shared-assets/react-admin/custom/content-editor/math-popover-button.tsx +0 -1
- package/dist/assets/shared-assets/react-admin/custom/content-editor/mobile-toolbar-content.tsx +4 -4
- package/dist/assets/shared-assets/react-admin/custom/content-editor/mode-toggle-button.tsx +1 -1
- package/dist/assets/shared-assets/react-admin/custom/content-editor/selection-bubble-menu.tsx +45 -8
- package/dist/assets/shared-assets/react-admin/custom/content-editor/slash-command-menu.tsx +153 -27
- package/dist/assets/shared-assets/react-admin/custom/content-editor/source-mode-dropdown-button.tsx +1 -1
- package/dist/assets/shared-assets/react-admin/custom/content-editor/source-mode.tsx +9 -11
- package/dist/assets/shared-assets/react-admin/custom/content-editor/table-bubble-menu.tsx +1 -1
- package/dist/assets/shared-assets/react-admin/custom/content-editor/table-button.tsx +24 -67
- package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/blockquote-button/blockquote-button.tsx +1 -1
- package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/code-block-button/code-block-button.tsx +1 -1
- package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/color-highlight-popover/color-highlight-popover-button.tsx +1 -1
- package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/color-highlight-popover/color-highlight-popover-content.tsx +1 -1
- package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/color-highlight-popover/color-highlight-popover.tsx +11 -2
- package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/color-highlight-popover/source-color-highlight-popover.tsx +1 -1
- package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/heading-dropdown-menu/heading-dropdown-menu-item.tsx +1 -1
- package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/heading-dropdown-menu/heading-dropdown-menu.tsx +9 -4
- package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/link-popover/link-button.tsx +1 -1
- package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/link-popover/link-control-popover.tsx +5 -12
- package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/link-popover/link-main.tsx +19 -45
- package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/link-popover/link-popover.tsx +2 -3
- package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/list-dropdown-menu/list-dropdown-menu-item.tsx +1 -1
- package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/list-dropdown-menu/list-dropdown-menu.tsx +9 -4
- package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/mark-button/mark-button.tsx +1 -1
- package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/undo-redo-button/undo-redo-button.tsx +1 -1
- package/dist/assets/shared-assets/react-admin/custom/date-range-picker.tsx +2 -2
- package/dist/assets/shared-assets/react-admin/custom/dynamic-list-field.tsx +20 -8
- package/dist/assets/shared-assets/react-admin/custom/media-gallery-field.tsx +1 -1
- package/dist/assets/shared-assets/react-admin/custom/nested-object-list-field.tsx +211 -0
- package/dist/assets/shared-assets/react-admin/schema.json +136 -23
- package/dist/assets/shared-assets/react-admin/ui/button.tsx +18 -18
- package/dist/assets/shared-assets/react-admin/ui/card.tsx +1 -1
- package/dist/assets/shared-assets/react-admin/ui/checkbox.tsx +1 -1
- package/dist/assets/shared-assets/react-admin/ui/combobox.tsx +27 -3
- package/dist/assets/shared-assets/react-admin/ui/drawer.tsx +9 -5
- package/dist/assets/shared-assets/react-admin/ui/dropdown-menu.tsx +27 -12
- package/dist/assets/shared-assets/react-admin/ui/form.tsx +1 -1
- package/dist/assets/shared-assets/react-admin/ui/sidebar.tsx +4 -4
- package/dist/cli.js +5396 -2385
- package/dist/cli.js.map +1 -1
- package/package.json +10 -2
|
@@ -39,6 +39,7 @@ export function ContentEditor({
|
|
|
39
39
|
|
|
40
40
|
const {
|
|
41
41
|
activeSlashIndex,
|
|
42
|
+
closeSlashMenu,
|
|
42
43
|
editor,
|
|
43
44
|
filteredSlashCommands,
|
|
44
45
|
handleModeChange,
|
|
@@ -47,7 +48,9 @@ export function ContentEditor({
|
|
|
47
48
|
mode,
|
|
48
49
|
selectSlashCommand,
|
|
49
50
|
setActiveSlashIndex,
|
|
51
|
+
setSlashSearch,
|
|
50
52
|
slashMenu,
|
|
53
|
+
slashSearchQuery,
|
|
51
54
|
sourceValue,
|
|
52
55
|
sourceSelectionRequest
|
|
53
56
|
} = useContentEditor({
|
|
@@ -123,8 +126,11 @@ export function ContentEditor({
|
|
|
123
126
|
activeIndex={activeSlashIndex}
|
|
124
127
|
commands={filteredSlashCommands}
|
|
125
128
|
onActiveIndexChange={setActiveSlashIndex}
|
|
129
|
+
onClose={closeSlashMenu}
|
|
126
130
|
onCommandSelect={selectSlashCommand}
|
|
131
|
+
onSearchChange={setSlashSearch}
|
|
127
132
|
slashMenu={slashMenu}
|
|
133
|
+
searchQuery={slashSearchQuery}
|
|
128
134
|
/>
|
|
129
135
|
|
|
130
136
|
<EditorContent
|
|
@@ -13,7 +13,7 @@ export function MathButton({ children, text, ...props }: MathButtonProps) {
|
|
|
13
13
|
<Button type="button" variant="ghost" size={text ? 'sm' : 'icon-sm'} {...props}>
|
|
14
14
|
{children ?? (
|
|
15
15
|
<>
|
|
16
|
-
<SigmaIcon
|
|
16
|
+
<SigmaIcon />
|
|
17
17
|
{text ? <span className="tiptap-button-text">{text}</span> : null}
|
|
18
18
|
</>
|
|
19
19
|
)}
|
package/dist/assets/shared-assets/react-admin/custom/content-editor/math-editor-controls.tsx
CHANGED
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
3
|
import { Button } from '@admin/components/ui/button'
|
|
4
|
-
import { Separator } from '@admin/components/ui/separator'
|
|
5
4
|
import { Textarea } from '@admin/components/ui/textarea'
|
|
6
|
-
import {
|
|
7
|
-
import { CornerDownLeftIcon, TrashIcon } from 'lucide-react'
|
|
5
|
+
import { TrashIcon } from 'lucide-react'
|
|
8
6
|
import * as React from 'react'
|
|
9
7
|
|
|
10
8
|
export interface MathEditorControlsProps {
|
|
11
9
|
disabled?: boolean
|
|
12
10
|
initialValue?: string
|
|
13
|
-
layout?: 'popover' | 'toolbar'
|
|
14
11
|
onApply: (value: string) => boolean | undefined
|
|
15
12
|
onComplete?: () => void
|
|
16
13
|
onRemove?: () => boolean | undefined
|
|
@@ -19,14 +16,19 @@ export interface MathEditorControlsProps {
|
|
|
19
16
|
export function MathEditorControls({
|
|
20
17
|
disabled,
|
|
21
18
|
initialValue = '',
|
|
22
|
-
layout = 'toolbar',
|
|
23
19
|
onApply,
|
|
24
20
|
onComplete,
|
|
25
21
|
onRemove
|
|
26
22
|
}: MathEditorControlsProps) {
|
|
27
23
|
const [value, setValue] = React.useState(initialValue)
|
|
24
|
+
const textareaRef = React.useRef<HTMLTextAreaElement>(null)
|
|
28
25
|
const canApply = Boolean(value.trim()) && !disabled
|
|
29
|
-
|
|
26
|
+
|
|
27
|
+
React.useEffect(() => {
|
|
28
|
+
if (disabled) return
|
|
29
|
+
|
|
30
|
+
textareaRef.current?.focus({ preventScroll: true })
|
|
31
|
+
}, [disabled])
|
|
30
32
|
|
|
31
33
|
const applyMath = React.useCallback(() => {
|
|
32
34
|
if (!canApply) return
|
|
@@ -60,57 +62,42 @@ export function MathEditorControls({
|
|
|
60
62
|
)
|
|
61
63
|
|
|
62
64
|
return (
|
|
63
|
-
<div
|
|
64
|
-
className={
|
|
65
|
-
isPopoverLayout
|
|
66
|
-
? 'flex w-72 max-w-[calc(100vw-2rem)] flex-col gap-3 p-3'
|
|
67
|
-
: 'flex min-w-72 max-w-[calc(100vw-2rem)] items-start gap-2 p-2'
|
|
68
|
-
}
|
|
69
|
-
>
|
|
65
|
+
<div className="flex w-72 flex-col gap-3 p-4">
|
|
70
66
|
<Textarea
|
|
67
|
+
ref={textareaRef}
|
|
71
68
|
value={value}
|
|
72
69
|
onChange={(event) => setValue(event.target.value)}
|
|
73
70
|
onKeyDown={handleKeyDown}
|
|
74
71
|
placeholder="Paste LaTeX..."
|
|
75
72
|
disabled={disabled}
|
|
76
|
-
autoFocus
|
|
77
73
|
className="min-h-24 resize-y rounded-md text-sm"
|
|
78
74
|
/>
|
|
79
75
|
|
|
80
|
-
<div
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
76
|
+
<div className="flex items-center flex-wrap gap-1">
|
|
77
|
+
{onRemove ? (
|
|
78
|
+
<Button
|
|
79
|
+
type="button"
|
|
80
|
+
variant="destructive"
|
|
81
|
+
disabled={disabled}
|
|
82
|
+
aria-label="Remove math"
|
|
83
|
+
title="Remove"
|
|
84
|
+
onClick={removeMath}
|
|
85
|
+
>
|
|
86
|
+
<TrashIcon />
|
|
87
|
+
</Button>
|
|
88
|
+
) : null}
|
|
89
|
+
|
|
86
90
|
<Button
|
|
87
91
|
type="button"
|
|
88
|
-
variant=
|
|
89
|
-
|
|
92
|
+
variant="default"
|
|
93
|
+
className="flex-1"
|
|
90
94
|
disabled={!canApply}
|
|
91
95
|
aria-label="Enter math"
|
|
92
96
|
title="Enter"
|
|
93
97
|
onClick={applyMath}
|
|
94
98
|
>
|
|
95
|
-
|
|
99
|
+
Insert
|
|
96
100
|
</Button>
|
|
97
|
-
|
|
98
|
-
{onRemove ? (
|
|
99
|
-
<>
|
|
100
|
-
<Separator orientation="vertical" decorative className="h-6 my-auto self-center" />
|
|
101
|
-
<Button
|
|
102
|
-
type="button"
|
|
103
|
-
variant="ghost"
|
|
104
|
-
size="icon-sm"
|
|
105
|
-
disabled={disabled}
|
|
106
|
-
aria-label="Remove math"
|
|
107
|
-
title="Remove"
|
|
108
|
-
onClick={removeMath}
|
|
109
|
-
>
|
|
110
|
-
<TrashIcon className="tiptap-button-icon" />
|
|
111
|
-
</Button>
|
|
112
|
-
</>
|
|
113
|
-
) : null}
|
|
114
101
|
</div>
|
|
115
102
|
</div>
|
|
116
103
|
)
|
package/dist/assets/shared-assets/react-admin/custom/content-editor/mobile-toolbar-content.tsx
CHANGED
|
@@ -27,13 +27,13 @@ export function MobileToolbarContent({
|
|
|
27
27
|
aria-label="Back to formatting toolbar"
|
|
28
28
|
title="Back"
|
|
29
29
|
>
|
|
30
|
-
<ArrowLeftIcon
|
|
30
|
+
<ArrowLeftIcon />
|
|
31
31
|
{type === 'highlighter' ? (
|
|
32
|
-
<HighlighterIcon
|
|
32
|
+
<HighlighterIcon />
|
|
33
33
|
) : type === 'math' ? (
|
|
34
|
-
<SigmaIcon
|
|
34
|
+
<SigmaIcon />
|
|
35
35
|
) : (
|
|
36
|
-
<LinkIcon
|
|
36
|
+
<LinkIcon />
|
|
37
37
|
)}
|
|
38
38
|
</Button>
|
|
39
39
|
</div>
|
package/dist/assets/shared-assets/react-admin/custom/content-editor/selection-bubble-menu.tsx
CHANGED
|
@@ -10,6 +10,7 @@ import { Separator } from '@admin/components/ui/separator'
|
|
|
10
10
|
import { cn } from '@admin/utils/shared/cn'
|
|
11
11
|
import type { Editor } from '@tiptap/core'
|
|
12
12
|
import { BubbleMenu } from '@tiptap/react/menus'
|
|
13
|
+
import * as React from 'react'
|
|
13
14
|
|
|
14
15
|
const HIDDEN_SELECTION_TOOLBAR_TYPES = [
|
|
15
16
|
'mediaGalleryPlaceholder',
|
|
@@ -26,6 +27,31 @@ const HIDDEN_SELECTION_TOOLBAR_TYPES = [
|
|
|
26
27
|
] as const
|
|
27
28
|
|
|
28
29
|
export function SelectionBubbleMenu({ editor }: { editor: Editor }) {
|
|
30
|
+
const [hasOpenSelectionMenuChild, setHasOpenSelectionMenuChild] = React.useState(false)
|
|
31
|
+
const hasOpenSelectionMenuChildRef = React.useRef(false)
|
|
32
|
+
const setSelectionMenuChildOpen = React.useCallback((isOpen: boolean) => {
|
|
33
|
+
hasOpenSelectionMenuChildRef.current = isOpen
|
|
34
|
+
setHasOpenSelectionMenuChild(isOpen)
|
|
35
|
+
}, [])
|
|
36
|
+
const handleSelectionMenuChildOpenChange = React.useCallback(
|
|
37
|
+
(isOpen: boolean) => {
|
|
38
|
+
setSelectionMenuChildOpen(isOpen)
|
|
39
|
+
},
|
|
40
|
+
[setSelectionMenuChildOpen]
|
|
41
|
+
)
|
|
42
|
+
const handleSelectionMenuPointerDown = React.useCallback(
|
|
43
|
+
(event: React.PointerEvent<HTMLDivElement>) => {
|
|
44
|
+
const target = event.target
|
|
45
|
+
if (!(target instanceof Element)) return
|
|
46
|
+
|
|
47
|
+
const menuTrigger = target.closest('button[aria-haspopup], button[aria-expanded]')
|
|
48
|
+
if (menuTrigger) {
|
|
49
|
+
setSelectionMenuChildOpen(true)
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
[setSelectionMenuChildOpen]
|
|
53
|
+
)
|
|
54
|
+
|
|
29
55
|
return (
|
|
30
56
|
<>
|
|
31
57
|
<BubbleMenu
|
|
@@ -46,8 +72,8 @@ export function SelectionBubbleMenu({ editor }: { editor: Editor }) {
|
|
|
46
72
|
role="toolbar"
|
|
47
73
|
aria-label="Link formatting toolbar"
|
|
48
74
|
className={cn(
|
|
49
|
-
'flex items-center gap-1 overflow-x-auto border-border
|
|
50
|
-
'relative z-
|
|
75
|
+
'flex items-center gap-1 overflow-x-auto border-border border bg-popover p-4 text-foreground shadow-2xl [scrollbar-width:none] [&::-webkit-scrollbar]:hidden rounded-lg',
|
|
76
|
+
'relative z-10'
|
|
51
77
|
)}
|
|
52
78
|
>
|
|
53
79
|
<LinkContent editor={editor} />
|
|
@@ -60,7 +86,7 @@ export function SelectionBubbleMenu({ editor }: { editor: Editor }) {
|
|
|
60
86
|
updateDelay={0}
|
|
61
87
|
shouldShow={({ editor: currentEditor, from, to }) =>
|
|
62
88
|
currentEditor.isEditable &&
|
|
63
|
-
from !== to &&
|
|
89
|
+
(from !== to || hasOpenSelectionMenuChild || hasOpenSelectionMenuChildRef.current) &&
|
|
64
90
|
HIDDEN_SELECTION_TOOLBAR_TYPES.every((type) => !currentEditor.isActive(type))
|
|
65
91
|
}
|
|
66
92
|
options={{
|
|
@@ -73,14 +99,22 @@ export function SelectionBubbleMenu({ editor }: { editor: Editor }) {
|
|
|
73
99
|
<div
|
|
74
100
|
role="toolbar"
|
|
75
101
|
aria-label="Selection formatting toolbar"
|
|
102
|
+
onPointerDownCapture={handleSelectionMenuPointerDown}
|
|
76
103
|
className={cn(
|
|
77
104
|
'flex items-center gap-1 overflow-x-auto border-border rounded-full border bg-popover p-1 text-popover-foreground shadow-menu [scrollbar-width:none] [&::-webkit-scrollbar]:hidden',
|
|
78
|
-
'relative z-
|
|
105
|
+
'relative z-10'
|
|
79
106
|
)}
|
|
80
107
|
>
|
|
81
108
|
<div className="flex items-center gap-0.5">
|
|
82
|
-
<HeadingDropdownMenu
|
|
83
|
-
|
|
109
|
+
<HeadingDropdownMenu
|
|
110
|
+
editor={editor}
|
|
111
|
+
onOpenChange={handleSelectionMenuChildOpenChange}
|
|
112
|
+
/>
|
|
113
|
+
<ListDropdownMenu
|
|
114
|
+
editor={editor}
|
|
115
|
+
types={['bulletList', 'orderedList', 'taskList']}
|
|
116
|
+
onOpenChange={handleSelectionMenuChildOpenChange}
|
|
117
|
+
/>
|
|
84
118
|
</div>
|
|
85
119
|
|
|
86
120
|
<Separator
|
|
@@ -95,8 +129,11 @@ export function SelectionBubbleMenu({ editor }: { editor: Editor }) {
|
|
|
95
129
|
<MarkButton editor={editor} type="strike" />
|
|
96
130
|
<MarkButton editor={editor} type="code" />
|
|
97
131
|
<MarkButton editor={editor} type="underline" />
|
|
98
|
-
<ColorHighlightPopover
|
|
99
|
-
|
|
132
|
+
<ColorHighlightPopover
|
|
133
|
+
editor={editor}
|
|
134
|
+
onOpenChange={handleSelectionMenuChildOpenChange}
|
|
135
|
+
/>
|
|
136
|
+
<LinkPopover editor={editor} onOpenChange={handleSelectionMenuChildOpenChange} />
|
|
100
137
|
</div>
|
|
101
138
|
</div>
|
|
102
139
|
</BubbleMenu>
|
|
@@ -1,65 +1,191 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
Combobox,
|
|
5
|
+
ComboboxContent,
|
|
6
|
+
ComboboxEmpty,
|
|
7
|
+
ComboboxInput,
|
|
8
|
+
ComboboxItem,
|
|
9
|
+
ComboboxList
|
|
10
|
+
} from '@admin/components/ui/combobox'
|
|
1
11
|
import type { ContentEditorSlashCommand } from '@admin/utils/editor/slash-commands'
|
|
2
|
-
import {
|
|
12
|
+
import type { SlashMenuState } from '@admin/utils/editor/tiptap'
|
|
3
13
|
import { cn } from '@admin/utils/shared/cn'
|
|
14
|
+
import * as React from 'react'
|
|
4
15
|
|
|
5
16
|
export function SlashCommandMenu({
|
|
6
17
|
activeIndex,
|
|
7
18
|
commands,
|
|
8
19
|
onActiveIndexChange,
|
|
20
|
+
onClose,
|
|
9
21
|
onCommandSelect,
|
|
10
|
-
|
|
22
|
+
onSearchChange,
|
|
23
|
+
slashMenu,
|
|
24
|
+
searchQuery
|
|
11
25
|
}: {
|
|
12
26
|
activeIndex: number
|
|
13
27
|
commands: ContentEditorSlashCommand[]
|
|
14
28
|
onActiveIndexChange: (index: number) => void
|
|
29
|
+
onClose: () => void
|
|
15
30
|
onCommandSelect: (command: ContentEditorSlashCommand) => void
|
|
31
|
+
onSearchChange: (query: string) => void
|
|
16
32
|
slashMenu: SlashMenuState | null
|
|
33
|
+
searchQuery: string
|
|
17
34
|
}) {
|
|
35
|
+
const activeItemRef = React.useRef<HTMLDivElement | null>(null)
|
|
36
|
+
const searchInputRef = React.useRef<HTMLInputElement | null>(null)
|
|
37
|
+
const activeCommandId = commands[activeIndex]?.id
|
|
38
|
+
|
|
39
|
+
const handleSearchKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
|
|
40
|
+
const stopComboboxKeyHandling = () => {
|
|
41
|
+
const baseUIEvent = event as React.KeyboardEvent<HTMLInputElement> & {
|
|
42
|
+
preventBaseUIHandler?: () => void
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
event.preventDefault()
|
|
46
|
+
event.stopPropagation()
|
|
47
|
+
baseUIEvent.preventBaseUIHandler?.()
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (event.key === 'ArrowDown') {
|
|
51
|
+
stopComboboxKeyHandling()
|
|
52
|
+
onActiveIndexChange(commands.length > 0 ? (activeIndex + 1) % commands.length : 0)
|
|
53
|
+
return
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (event.key === 'ArrowUp') {
|
|
57
|
+
stopComboboxKeyHandling()
|
|
58
|
+
onActiveIndexChange(
|
|
59
|
+
commands.length > 0 ? (activeIndex - 1 + commands.length) % commands.length : 0
|
|
60
|
+
)
|
|
61
|
+
return
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (event.key === 'Escape') {
|
|
65
|
+
stopComboboxKeyHandling()
|
|
66
|
+
onClose()
|
|
67
|
+
return
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (event.key === 'Enter' || event.key === 'Tab') {
|
|
71
|
+
const command = commands[activeIndex]
|
|
72
|
+
if (!command) return
|
|
73
|
+
|
|
74
|
+
stopComboboxKeyHandling()
|
|
75
|
+
onCommandSelect(command)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
React.useEffect(() => {
|
|
80
|
+
if (!slashMenu) return
|
|
81
|
+
|
|
82
|
+
const focusSearchInput = () => {
|
|
83
|
+
searchInputRef.current?.focus({ preventScroll: true })
|
|
84
|
+
searchInputRef.current?.select()
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
focusSearchInput()
|
|
88
|
+
const frame = requestAnimationFrame(focusSearchInput)
|
|
89
|
+
|
|
90
|
+
return () => cancelAnimationFrame(frame)
|
|
91
|
+
}, [slashMenu])
|
|
92
|
+
|
|
93
|
+
React.useEffect(() => {
|
|
94
|
+
if (!activeCommandId) return
|
|
95
|
+
|
|
96
|
+
activeItemRef.current?.scrollIntoView({ block: 'nearest' })
|
|
97
|
+
}, [activeCommandId])
|
|
98
|
+
|
|
18
99
|
if (!slashMenu) return null
|
|
19
100
|
|
|
101
|
+
const slashAnchor = {
|
|
102
|
+
getBoundingClientRect: () => slashMenu.rect
|
|
103
|
+
}
|
|
104
|
+
|
|
20
105
|
return (
|
|
21
|
-
<
|
|
22
|
-
|
|
23
|
-
|
|
106
|
+
<Combobox<ContentEditorSlashCommand>
|
|
107
|
+
autoHighlight
|
|
108
|
+
filter={null}
|
|
109
|
+
filteredItems={commands}
|
|
110
|
+
inputValue={searchQuery}
|
|
111
|
+
itemToStringLabel={(command) => command.title}
|
|
112
|
+
itemToStringValue={(command) => command.id}
|
|
113
|
+
items={commands}
|
|
114
|
+
onInputValueChange={onSearchChange}
|
|
115
|
+
onItemHighlighted={(command) => {
|
|
116
|
+
if (!command) return
|
|
117
|
+
|
|
118
|
+
const nextIndex = commands.findIndex((item) => item.id === command.id)
|
|
119
|
+
if (nextIndex >= 0) onActiveIndexChange(nextIndex)
|
|
120
|
+
}}
|
|
121
|
+
onOpenChange={(isOpen) => {
|
|
122
|
+
if (!isOpen) onClose()
|
|
123
|
+
}}
|
|
124
|
+
onValueChange={(command) => {
|
|
125
|
+
if (command) onCommandSelect(command)
|
|
126
|
+
}}
|
|
127
|
+
open
|
|
128
|
+
value={null}
|
|
24
129
|
>
|
|
25
|
-
|
|
26
|
-
|
|
130
|
+
<ComboboxContent
|
|
131
|
+
anchor={slashAnchor}
|
|
132
|
+
align="start"
|
|
133
|
+
className="w-72 min-w-72 max-w-[calc(100vw-2rem)] p-2 gap-2 flex flex-col"
|
|
134
|
+
collisionPadding={8}
|
|
135
|
+
finalFocus={false}
|
|
136
|
+
initialFocus={false}
|
|
137
|
+
positionMethod="fixed"
|
|
138
|
+
side="bottom"
|
|
139
|
+
sideOffset={8}
|
|
140
|
+
data-content-editor-slash-menu
|
|
141
|
+
>
|
|
142
|
+
<ComboboxInput
|
|
143
|
+
aria-label="Search commands"
|
|
144
|
+
ref={searchInputRef}
|
|
145
|
+
onKeyDown={handleSearchKeyDown}
|
|
146
|
+
placeholder="Search commands..."
|
|
147
|
+
showClear
|
|
148
|
+
showTrigger={false}
|
|
149
|
+
className="border border-border"
|
|
150
|
+
/>
|
|
151
|
+
<ComboboxEmpty className="py-6">No commands found</ComboboxEmpty>
|
|
152
|
+
<ComboboxList className="max-h-80 p-0">
|
|
27
153
|
{commands.map((command, index) => {
|
|
28
154
|
const Icon = command.Icon
|
|
29
155
|
const isActive = index === activeIndex
|
|
30
156
|
|
|
31
157
|
return (
|
|
32
|
-
<
|
|
158
|
+
<ComboboxItem
|
|
159
|
+
index={index}
|
|
33
160
|
key={command.id}
|
|
34
|
-
|
|
161
|
+
data-active={isActive ? 'true' : undefined}
|
|
162
|
+
ref={(node) => {
|
|
163
|
+
if (isActive) activeItemRef.current = node
|
|
164
|
+
}}
|
|
165
|
+
onMouseDown={(event) => event.preventDefault()}
|
|
166
|
+
onMouseEnter={() => onActiveIndexChange(index)}
|
|
167
|
+
value={command}
|
|
35
168
|
className={cn(
|
|
36
|
-
'
|
|
169
|
+
'border border-transparent p-2 data-highlighted:bg-card! data-highlighted:border-border!',
|
|
37
170
|
{
|
|
38
|
-
'bg-
|
|
39
|
-
'text-popover-foreground hover:bg-accent hover:text-accent-foreground':
|
|
40
|
-
!isActive
|
|
171
|
+
'bg-card border-border shadow-input text-foreground': isActive
|
|
41
172
|
}
|
|
42
173
|
)}
|
|
43
|
-
onMouseDown={(event) => event.preventDefault()}
|
|
44
|
-
onMouseEnter={() => onActiveIndexChange(index)}
|
|
45
|
-
onClick={() => onCommandSelect(command)}
|
|
46
174
|
>
|
|
47
|
-
<span className="
|
|
48
|
-
<Icon
|
|
175
|
+
<span className="flex size-8 shrink-0 items-center justify-center rounded-sm bg-card border border-border shadow-input">
|
|
176
|
+
<Icon />
|
|
49
177
|
</span>
|
|
50
|
-
<span className="min-w-0">
|
|
51
|
-
<span className="
|
|
52
|
-
<span className="
|
|
178
|
+
<span className="min-w-0 space-y-1 flex flex-col w-full">
|
|
179
|
+
<span className="font-medium leading-none">{command.title}</span>
|
|
180
|
+
<span className="truncate text-xs text-muted-foreground">
|
|
53
181
|
{command.description}
|
|
54
182
|
</span>
|
|
55
183
|
</span>
|
|
56
|
-
</
|
|
184
|
+
</ComboboxItem>
|
|
57
185
|
)
|
|
58
186
|
})}
|
|
59
|
-
</
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
)}
|
|
63
|
-
</div>
|
|
187
|
+
</ComboboxList>
|
|
188
|
+
</ComboboxContent>
|
|
189
|
+
</Combobox>
|
|
64
190
|
)
|
|
65
191
|
}
|
package/dist/assets/shared-assets/react-admin/custom/content-editor/source-mode-dropdown-button.tsx
CHANGED
|
@@ -40,7 +40,7 @@ export function SourceModeDropdownButton({
|
|
|
40
40
|
onPointerDown={(event) => event.preventDefault()}
|
|
41
41
|
onClick={() => setIsOpen((current) => !current)}
|
|
42
42
|
>
|
|
43
|
-
<Icon
|
|
43
|
+
<Icon />
|
|
44
44
|
<ChevronDownIcon className="tiptap-button-dropdown-small" />
|
|
45
45
|
</Button>
|
|
46
46
|
</DropdownMenuTrigger>
|
|
@@ -105,7 +105,6 @@ export function SourceMode({
|
|
|
105
105
|
isMarkdownLinkActive,
|
|
106
106
|
markdownLinkUrl,
|
|
107
107
|
openMediaGalleryAtCursor,
|
|
108
|
-
openMarkdownLink,
|
|
109
108
|
redoMarkdownChange,
|
|
110
109
|
removeHighlight,
|
|
111
110
|
removeMarkdownLink,
|
|
@@ -140,7 +139,7 @@ export function SourceMode({
|
|
|
140
139
|
aria-label="Undo"
|
|
141
140
|
title="Undo"
|
|
142
141
|
>
|
|
143
|
-
<Undo2Icon
|
|
142
|
+
<Undo2Icon />
|
|
144
143
|
</Button>
|
|
145
144
|
<Button
|
|
146
145
|
type="button"
|
|
@@ -151,7 +150,7 @@ export function SourceMode({
|
|
|
151
150
|
aria-label="Redo"
|
|
152
151
|
title="Redo"
|
|
153
152
|
>
|
|
154
|
-
<Redo2Icon
|
|
153
|
+
<Redo2Icon />
|
|
155
154
|
</Button>
|
|
156
155
|
</div>
|
|
157
156
|
|
|
@@ -178,7 +177,7 @@ export function SourceMode({
|
|
|
178
177
|
title={option.label}
|
|
179
178
|
onSelect={() => applyHeading(option.level)}
|
|
180
179
|
>
|
|
181
|
-
<Icon
|
|
180
|
+
<Icon />
|
|
182
181
|
<span className="tiptap-button-text">{option.label}</span>
|
|
183
182
|
</DropdownMenuItem>
|
|
184
183
|
)
|
|
@@ -196,7 +195,7 @@ export function SourceMode({
|
|
|
196
195
|
title={option.label}
|
|
197
196
|
onSelect={() => applyList(option.type)}
|
|
198
197
|
>
|
|
199
|
-
<Icon
|
|
198
|
+
<Icon />
|
|
200
199
|
<span className="tiptap-button-text">{option.label}</span>
|
|
201
200
|
</DropdownMenuItem>
|
|
202
201
|
)
|
|
@@ -211,7 +210,7 @@ export function SourceMode({
|
|
|
211
210
|
aria-label="Blockquote"
|
|
212
211
|
title="Blockquote"
|
|
213
212
|
>
|
|
214
|
-
<BlockquoteIcon
|
|
213
|
+
<BlockquoteIcon />
|
|
215
214
|
</Button>
|
|
216
215
|
<Button
|
|
217
216
|
type="button"
|
|
@@ -222,7 +221,7 @@ export function SourceMode({
|
|
|
222
221
|
aria-label="Code Block"
|
|
223
222
|
title="Code Block"
|
|
224
223
|
>
|
|
225
|
-
<CodeBlockIcon
|
|
224
|
+
<CodeBlockIcon />
|
|
226
225
|
</Button>
|
|
227
226
|
<Button
|
|
228
227
|
type="button"
|
|
@@ -233,7 +232,7 @@ export function SourceMode({
|
|
|
233
232
|
aria-label="Insert divider"
|
|
234
233
|
title="Divider"
|
|
235
234
|
>
|
|
236
|
-
<Minus
|
|
235
|
+
<Minus />
|
|
237
236
|
</Button>
|
|
238
237
|
</div>
|
|
239
238
|
|
|
@@ -281,7 +280,7 @@ export function SourceMode({
|
|
|
281
280
|
aria-label={mark.label}
|
|
282
281
|
title={mark.label}
|
|
283
282
|
>
|
|
284
|
-
<mark.Icon
|
|
283
|
+
<mark.Icon />
|
|
285
284
|
</Button>
|
|
286
285
|
))}
|
|
287
286
|
<SourceColorHighlightPopover
|
|
@@ -296,7 +295,6 @@ export function SourceMode({
|
|
|
296
295
|
disabled={disabled}
|
|
297
296
|
isActive={isMarkdownLinkActive}
|
|
298
297
|
onOpenChange={handleMarkdownLinkPopoverOpenChange}
|
|
299
|
-
openLink={openMarkdownLink}
|
|
300
298
|
removeLink={removeMarkdownLink}
|
|
301
299
|
setLink={setMarkdownLink}
|
|
302
300
|
setUrl={setMarkdownLinkUrl}
|
|
@@ -322,7 +320,7 @@ export function SourceMode({
|
|
|
322
320
|
aria-label="Add media"
|
|
323
321
|
title="Add media"
|
|
324
322
|
>
|
|
325
|
-
<ImagePlusIcon
|
|
323
|
+
<ImagePlusIcon />
|
|
326
324
|
<span className="tiptap-button-text">Media</span>
|
|
327
325
|
</Button>
|
|
328
326
|
</div>
|