@wakastellar/ui 3.3.3 → 3.5.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.
- package/dist/badge-BbwO7QeZ.js +1 -0
- package/dist/badge-BfiocODp.mjs +23 -0
- package/dist/charts.cjs.js +1 -1
- package/dist/charts.es.js +1 -1
- package/dist/chunk-14q5BKub.js +1 -0
- package/dist/{chunk-BH6uBOac.mjs → chunk-Cr9pTUWm.mjs} +5 -5
- package/dist/cn-DEtaFQsA.js +1 -0
- package/dist/cn-DUn6aSIQ.mjs +24 -0
- package/dist/doc.cjs.js +2 -2
- package/dist/doc.es.js +19 -19
- package/dist/editor.cjs.js +48 -0
- package/dist/editor.d.ts +1 -0
- package/dist/editor.es.js +6551 -0
- package/dist/{exceljs.min-DG9M8IZ1.mjs → exceljs.min-DL1XYDll.mjs} +1 -1
- package/dist/{exceljs.min-BuefmDRS.js → exceljs.min-qeIfSCbF.js} +1 -1
- package/dist/export.cjs.js +1 -1
- package/dist/export.es.js +1 -1
- package/dist/index.cjs.js +150 -150
- package/dist/index.es.js +26782 -27591
- package/dist/input-BfaSAGVw.js +1 -0
- package/dist/input-DVr_Qkl8.mjs +14 -0
- package/dist/rich-text.cjs.js +1 -1
- package/dist/rich-text.es.js +1 -1
- package/dist/security-CyBpuklN.mjs +122 -0
- package/dist/security-bFWwDrlg.js +1 -0
- package/dist/separator-NrkltulH.js +1 -0
- package/dist/separator-ibN2mycs.mjs +51 -0
- package/dist/src/components/editor/blocks/index.d.ts +51 -0
- package/dist/src/components/editor/blocks/waka-acceptance-criteria-block.d.ts +60 -0
- package/dist/src/components/editor/blocks/waka-ai-assist-block.d.ts +58 -0
- package/dist/src/components/editor/blocks/waka-api-endpoint-block.d.ts +63 -0
- package/dist/src/components/editor/blocks/waka-code-playground-block.d.ts +61 -0
- package/dist/src/components/editor/blocks/waka-comment-thread-block.d.ts +85 -0
- package/dist/src/components/editor/blocks/waka-diagram-block.d.ts +52 -0
- package/dist/src/components/editor/blocks/waka-embed-block.d.ts +58 -0
- package/dist/src/components/editor/blocks/waka-slash-menu-block.d.ts +67 -0
- package/dist/src/components/editor/blocks/waka-user-story-block.d.ts +79 -0
- package/dist/src/components/editor/blocks/waka-version-diff-block.d.ts +73 -0
- package/dist/src/components/editor/index.d.ts +66 -0
- package/dist/src/components/editor/waka-ai-writer.d.ts +80 -0
- package/dist/src/components/editor/waka-collaborative-editor.d.ts +93 -0
- package/dist/src/components/editor/waka-diff-viewer.d.ts +71 -0
- package/dist/src/components/editor/waka-dnd-editor.d.ts +64 -0
- package/dist/src/components/editor/waka-document-editor.d.ts +92 -0
- package/dist/src/components/editor/waka-editor-elements.d.ts +79 -0
- package/dist/src/components/editor/waka-editor-leaves.d.ts +39 -0
- package/dist/src/components/editor/waka-editor-plugins.d.ts +41 -0
- package/dist/src/components/editor/waka-editor-toolbar.d.ts +20 -0
- package/dist/src/components/editor/waka-editor.d.ts +59 -0
- package/dist/src/components/editor/waka-floating-toolbar.d.ts +47 -0
- package/dist/src/components/editor/waka-markdown-editor.d.ts +60 -0
- package/dist/src/components/editor/waka-mention-editor.d.ts +125 -0
- package/dist/src/components/editor/waka-slash-menu.d.ts +70 -0
- package/dist/src/components/editor/waka-spec-editor.d.ts +88 -0
- package/dist/src/components/index.d.ts +1 -15
- package/dist/src/editor.d.ts +26 -0
- package/dist/textarea-CdQWggYG.js +1 -0
- package/dist/textarea-DJDXJ3nd.mjs +23 -0
- package/dist/types-C2St0wOW.js +1 -0
- package/dist/{types-B6GVaSIP.mjs → types-JnqoLyuv.mjs} +214 -211
- package/dist/{useDataTableImport-BPvfo--2.mjs → useDataTableImport-BWUFesPi.mjs} +3 -3
- package/dist/{useDataTableImport-Cm_pCKnO.js → useDataTableImport-T7ddpN5k.js} +3 -3
- package/dist/waka-doc-renderer-CTxC7Trf.js +3 -0
- package/dist/{waka-doc-renderer-BkIvas3z.mjs → waka-doc-renderer-Cw-Xnyen.mjs} +264 -281
- package/dist/waka-editor-plugins-DR6tpsUC.mjs +135 -0
- package/dist/waka-editor-plugins-sGSh9hn2.js +1 -0
- package/dist/waka-rich-text-editor-BlIdtknG.js +1 -0
- package/dist/waka-rich-text-editor-D1uA3zbB.js +1 -0
- package/dist/waka-rich-text-editor-DgSWiXMW.mjs +342 -0
- package/dist/waka-rich-text-editor-DndVJuDw.mjs +2 -0
- package/package.json +87 -2
- package/src/blocks/footer/index.tsx +1 -6
- package/src/blocks/login/index.tsx +1 -7
- package/src/blocks/profile/index.tsx +3 -5
- package/src/components/editor/blocks/index.ts +182 -0
- package/src/components/editor/blocks/waka-acceptance-criteria-block.tsx +326 -0
- package/src/components/editor/blocks/waka-ai-assist-block.tsx +284 -0
- package/src/components/editor/blocks/waka-api-endpoint-block.tsx +382 -0
- package/src/components/editor/blocks/waka-code-playground-block.tsx +331 -0
- package/src/components/editor/blocks/waka-comment-thread-block.tsx +448 -0
- package/src/components/editor/blocks/waka-diagram-block.tsx +293 -0
- package/src/components/editor/blocks/waka-embed-block.tsx +416 -0
- package/src/components/editor/blocks/waka-slash-menu-block.tsx +432 -0
- package/src/components/editor/blocks/waka-user-story-block.tsx +295 -0
- package/src/components/editor/blocks/waka-version-diff-block.tsx +426 -0
- package/src/components/editor/index.ts +279 -0
- package/src/components/editor/waka-ai-writer.tsx +434 -0
- package/src/components/editor/waka-collaborative-editor.tsx +426 -0
- package/src/components/editor/waka-diff-viewer.tsx +352 -0
- package/src/components/editor/waka-dnd-editor.tsx +284 -0
- package/src/components/editor/waka-document-editor.tsx +502 -0
- package/src/components/editor/waka-editor-elements.tsx +312 -0
- package/src/components/editor/waka-editor-leaves.tsx +101 -0
- package/src/components/editor/waka-editor-plugins.ts +207 -0
- package/src/components/editor/waka-editor-toolbar.tsx +358 -0
- package/src/components/editor/waka-editor.tsx +431 -0
- package/src/components/editor/waka-floating-toolbar.tsx +268 -0
- package/src/components/editor/waka-markdown-editor.tsx +395 -0
- package/src/components/editor/waka-mention-editor.tsx +459 -0
- package/src/components/editor/waka-slash-menu.tsx +392 -0
- package/src/components/editor/waka-spec-editor.tsx +657 -0
- package/src/components/index.ts +1 -18
- package/dist/chunk-BDDJmn7V.js +0 -1
- package/dist/cn-DnPbmOCy.js +0 -1
- package/dist/cn-DpLcAzrf.mjs +0 -22
- package/dist/separator-BDReXBvI.mjs +0 -59
- package/dist/separator-BKjNl9sI.js +0 -1
- package/dist/src/components/waka-actor-badge/index.d.ts +0 -8
- package/dist/src/components/waka-actors-list/index.d.ts +0 -18
- package/dist/src/components/waka-ai-assistant-button/index.d.ts +0 -8
- package/dist/src/components/waka-document-flyover/index.d.ts +0 -10
- package/dist/src/components/waka-document-preview-popup/index.d.ts +0 -26
- package/dist/src/components/waka-hour-balance-badge/index.d.ts +0 -8
- package/dist/src/components/waka-hour-consumption-table/index.d.ts +0 -15
- package/dist/src/components/waka-hour-pack-dialog/index.d.ts +0 -8
- package/dist/src/components/waka-project-stats-header/index.d.ts +0 -15
- package/dist/src/components/waka-step-comment-bubble/index.d.ts +0 -13
- package/dist/src/components/waka-step-comment-panel/index.d.ts +0 -20
- package/dist/src/components/waka-step-permission-matrix/index.d.ts +0 -12
- package/dist/src/components/waka-time-entry-dialog/index.d.ts +0 -16
- package/dist/src/components/waka-time-tracking-flyover/index.d.ts +0 -11
- package/dist/types-BH9cQRqZ.js +0 -1
- package/dist/waka-doc-renderer-BZ2-SqyT.js +0 -3
- package/dist/waka-rich-text-editor-BJGlQgpq.js +0 -1
- package/dist/waka-rich-text-editor-BJzzxeP1.mjs +0 -361
- package/dist/waka-rich-text-editor-wnXLwvUo.js +0 -1
- package/src/components/waka-actor-badge/index.tsx +0 -34
- package/src/components/waka-actors-list/index.tsx +0 -125
- package/src/components/waka-ai-assistant-button/index.tsx +0 -31
- package/src/components/waka-document-flyover/index.tsx +0 -36
- package/src/components/waka-document-preview-popup/index.tsx +0 -103
- package/src/components/waka-hour-balance-badge/index.tsx +0 -43
- package/src/components/waka-hour-consumption-table/index.tsx +0 -72
- package/src/components/waka-hour-pack-dialog/index.tsx +0 -72
- package/src/components/waka-project-stats-header/index.tsx +0 -69
- package/src/components/waka-step-comment-bubble/index.tsx +0 -71
- package/src/components/waka-step-comment-panel/index.tsx +0 -106
- package/src/components/waka-step-permission-matrix/index.tsx +0 -65
- package/src/components/waka-time-entry-dialog/index.tsx +0 -131
- package/src/components/waka-time-tracking-flyover/index.tsx +0 -41
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import {
|
|
5
|
+
Heading1,
|
|
6
|
+
Heading2,
|
|
7
|
+
Heading3,
|
|
8
|
+
Table,
|
|
9
|
+
Columns2,
|
|
10
|
+
Image,
|
|
11
|
+
Code,
|
|
12
|
+
Minus,
|
|
13
|
+
Quote,
|
|
14
|
+
MessageSquarePlus,
|
|
15
|
+
List,
|
|
16
|
+
ListOrdered,
|
|
17
|
+
Link,
|
|
18
|
+
Sparkles,
|
|
19
|
+
type LucideIcon,
|
|
20
|
+
} from "lucide-react"
|
|
21
|
+
import { cn } from "../../utils/cn"
|
|
22
|
+
|
|
23
|
+
// ─── Types ───────────────────────────────────────────────────────────────────
|
|
24
|
+
|
|
25
|
+
/** A single slash command entry */
|
|
26
|
+
export interface SlashCommand {
|
|
27
|
+
/** Unique key used for filtering */
|
|
28
|
+
key: string
|
|
29
|
+
/** Display label */
|
|
30
|
+
label: string
|
|
31
|
+
/** Optional description shown below the label */
|
|
32
|
+
description?: string
|
|
33
|
+
/** Lucide icon component */
|
|
34
|
+
icon: LucideIcon
|
|
35
|
+
/** Group name (commands are visually grouped) */
|
|
36
|
+
group: string
|
|
37
|
+
/** Search keywords for filtering */
|
|
38
|
+
keywords?: string[]
|
|
39
|
+
/**
|
|
40
|
+
* Action to execute when the command is selected.
|
|
41
|
+
* Receives the Plate editor instance.
|
|
42
|
+
*/
|
|
43
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
44
|
+
action: (editor: any) => void
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface WakaSlashMenuProps {
|
|
48
|
+
/** Commands to display in the menu */
|
|
49
|
+
commands?: SlashCommand[]
|
|
50
|
+
/** CSS class for the menu container */
|
|
51
|
+
className?: string
|
|
52
|
+
/** Callback when a command is selected. Receives the command key. */
|
|
53
|
+
onSelect?: (key: string) => void
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// ─── Default commands ────────────────────────────────────────────────────────
|
|
57
|
+
|
|
58
|
+
/** Default slash commands for WakaStart editors */
|
|
59
|
+
export const DEFAULT_SLASH_COMMANDS: SlashCommand[] = [
|
|
60
|
+
// Basic blocks
|
|
61
|
+
{
|
|
62
|
+
key: "h1",
|
|
63
|
+
label: "Titre 1",
|
|
64
|
+
description: "Titre principal de section",
|
|
65
|
+
icon: Heading1,
|
|
66
|
+
group: "Blocs de base",
|
|
67
|
+
keywords: ["heading", "titre", "h1"],
|
|
68
|
+
action: (e) => { try { e.tf.h1.toggle() } catch { /* */ } },
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
key: "h2",
|
|
72
|
+
label: "Titre 2",
|
|
73
|
+
description: "Sous-titre de section",
|
|
74
|
+
icon: Heading2,
|
|
75
|
+
group: "Blocs de base",
|
|
76
|
+
keywords: ["heading", "titre", "h2"],
|
|
77
|
+
action: (e) => { try { e.tf.h2.toggle() } catch { /* */ } },
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
key: "h3",
|
|
81
|
+
label: "Titre 3",
|
|
82
|
+
description: "Titre de sous-section",
|
|
83
|
+
icon: Heading3,
|
|
84
|
+
group: "Blocs de base",
|
|
85
|
+
keywords: ["heading", "titre", "h3"],
|
|
86
|
+
action: (e) => { try { e.tf.h3.toggle() } catch { /* */ } },
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
key: "bulletList",
|
|
90
|
+
label: "Liste a puces",
|
|
91
|
+
description: "Liste non ordonnee",
|
|
92
|
+
icon: List,
|
|
93
|
+
group: "Blocs de base",
|
|
94
|
+
keywords: ["bullet", "list", "unordered", "puces"],
|
|
95
|
+
action: (e) => { try { e.chain().focus().toggleBulletList().run() } catch { /* */ } },
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
key: "numberedList",
|
|
99
|
+
label: "Liste numerotee",
|
|
100
|
+
description: "Liste ordonnee",
|
|
101
|
+
icon: ListOrdered,
|
|
102
|
+
group: "Blocs de base",
|
|
103
|
+
keywords: ["numbered", "list", "ordered", "numerotee"],
|
|
104
|
+
action: (e) => { try { e.chain().focus().toggleOrderedList().run() } catch { /* */ } },
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
key: "blockquote",
|
|
108
|
+
label: "Citation",
|
|
109
|
+
description: "Bloc de citation",
|
|
110
|
+
icon: Quote,
|
|
111
|
+
group: "Blocs de base",
|
|
112
|
+
keywords: ["quote", "citation", "blockquote"],
|
|
113
|
+
action: (e) => { try { e.tf.blockquote.toggle() } catch { /* */ } },
|
|
114
|
+
},
|
|
115
|
+
// Advanced blocks
|
|
116
|
+
{
|
|
117
|
+
key: "table",
|
|
118
|
+
label: "Tableau",
|
|
119
|
+
description: "Inserer un tableau 3x3",
|
|
120
|
+
icon: Table,
|
|
121
|
+
group: "Blocs avances",
|
|
122
|
+
keywords: ["table", "tableau", "grid"],
|
|
123
|
+
action: (e) => { try { e.tf.table.insert({ rows: 3, cols: 3 }) } catch { /* */ } },
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
key: "columns",
|
|
127
|
+
label: "Colonnes",
|
|
128
|
+
description: "Mise en page multi-colonnes",
|
|
129
|
+
icon: Columns2,
|
|
130
|
+
group: "Blocs avances",
|
|
131
|
+
keywords: ["column", "colonne", "layout", "mise en page"],
|
|
132
|
+
action: (e) => { try { e.tf.column.insert() } catch { /* */ } },
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
key: "callout",
|
|
136
|
+
label: "Callout",
|
|
137
|
+
description: "Encart d'information colore",
|
|
138
|
+
icon: MessageSquarePlus,
|
|
139
|
+
group: "Blocs avances",
|
|
140
|
+
keywords: ["callout", "alert", "info", "encart"],
|
|
141
|
+
action: (e) => { try { e.tf.callout.insert() } catch { /* */ } },
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
key: "codeBlock",
|
|
145
|
+
label: "Bloc de code",
|
|
146
|
+
description: "Code source avec coloration",
|
|
147
|
+
icon: Code,
|
|
148
|
+
group: "Blocs avances",
|
|
149
|
+
keywords: ["code", "pre", "source"],
|
|
150
|
+
action: (e) => { try { e.chain().focus().toggleCodeBlock().run() } catch { /* */ } },
|
|
151
|
+
},
|
|
152
|
+
// Insertions
|
|
153
|
+
{
|
|
154
|
+
key: "link",
|
|
155
|
+
label: "Lien",
|
|
156
|
+
description: "Inserer un lien hypertexte",
|
|
157
|
+
icon: Link,
|
|
158
|
+
group: "Insertions",
|
|
159
|
+
keywords: ["link", "lien", "url", "href"],
|
|
160
|
+
action: (e) => { try { e.tf.link.toggle() } catch { /* */ } },
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
key: "image",
|
|
164
|
+
label: "Image",
|
|
165
|
+
description: "Inserer une image",
|
|
166
|
+
icon: Image,
|
|
167
|
+
group: "Insertions",
|
|
168
|
+
keywords: ["image", "photo", "picture", "img"],
|
|
169
|
+
action: (e) => { try { e.tf.image.insert({ url: "" }) } catch { /* */ } },
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
key: "divider",
|
|
173
|
+
label: "Separateur",
|
|
174
|
+
description: "Ligne horizontale de separation",
|
|
175
|
+
icon: Minus,
|
|
176
|
+
group: "Insertions",
|
|
177
|
+
keywords: ["divider", "separator", "hr", "line"],
|
|
178
|
+
action: (e) => { try { e.chain().focus().insertBreak().run() } catch { /* */ } },
|
|
179
|
+
},
|
|
180
|
+
// AI
|
|
181
|
+
{
|
|
182
|
+
key: "ai",
|
|
183
|
+
label: "Demander a l'IA",
|
|
184
|
+
description: "Generer du contenu avec l'assistant IA",
|
|
185
|
+
icon: Sparkles,
|
|
186
|
+
group: "IA",
|
|
187
|
+
keywords: ["ai", "ia", "assistant", "generate", "generer"],
|
|
188
|
+
action: () => {
|
|
189
|
+
// AI action is typically handled by the AI plugin
|
|
190
|
+
// This acts as a trigger for the AIChatPlugin
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
]
|
|
194
|
+
|
|
195
|
+
// ─── Slash menu rendering component ─────────────────────────────────────────
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* WakaSlashMenu -- Configurable slash command palette.
|
|
199
|
+
*
|
|
200
|
+
* This component is designed to be used **inside** a Plate editor context
|
|
201
|
+
* as the combobox renderer for the `@platejs/combobox` SlashPlugin,
|
|
202
|
+
* or standalone as a command registry.
|
|
203
|
+
*
|
|
204
|
+
* When used standalone, provide `onSelect` to receive the selected command key.
|
|
205
|
+
* The consuming app is responsible for connecting it to the editor.
|
|
206
|
+
*
|
|
207
|
+
* @example
|
|
208
|
+
* ```tsx
|
|
209
|
+
* <WakaSlashMenu
|
|
210
|
+
* commands={[...DEFAULT_SLASH_COMMANDS, myCustomCommand]}
|
|
211
|
+
* onSelect={(key) => console.log("Selected:", key)}
|
|
212
|
+
* />
|
|
213
|
+
* ```
|
|
214
|
+
*/
|
|
215
|
+
export function WakaSlashMenu({
|
|
216
|
+
commands = DEFAULT_SLASH_COMMANDS,
|
|
217
|
+
className,
|
|
218
|
+
onSelect,
|
|
219
|
+
}: WakaSlashMenuProps) {
|
|
220
|
+
const [search, setSearch] = React.useState("")
|
|
221
|
+
const [activeIndex, setActiveIndex] = React.useState(0)
|
|
222
|
+
const listRef = React.useRef<HTMLDivElement>(null)
|
|
223
|
+
|
|
224
|
+
// Filter commands
|
|
225
|
+
const filtered = React.useMemo(() => {
|
|
226
|
+
if (!search) return commands
|
|
227
|
+
const q = search.toLowerCase()
|
|
228
|
+
return commands.filter(
|
|
229
|
+
(cmd) =>
|
|
230
|
+
cmd.label.toLowerCase().includes(q) ||
|
|
231
|
+
cmd.key.toLowerCase().includes(q) ||
|
|
232
|
+
cmd.keywords?.some((kw) => kw.toLowerCase().includes(q))
|
|
233
|
+
)
|
|
234
|
+
}, [commands, search])
|
|
235
|
+
|
|
236
|
+
// Group commands
|
|
237
|
+
const groups = React.useMemo(() => {
|
|
238
|
+
const map = new Map<string, SlashCommand[]>()
|
|
239
|
+
for (const cmd of filtered) {
|
|
240
|
+
const group = cmd.group || "Autre"
|
|
241
|
+
if (!map.has(group)) map.set(group, [])
|
|
242
|
+
map.get(group)!.push(cmd)
|
|
243
|
+
}
|
|
244
|
+
return map
|
|
245
|
+
}, [filtered])
|
|
246
|
+
|
|
247
|
+
// Flatten for keyboard nav
|
|
248
|
+
const flatItems = React.useMemo(() => filtered, [filtered])
|
|
249
|
+
|
|
250
|
+
// Keyboard navigation
|
|
251
|
+
const handleKeyDown = React.useCallback(
|
|
252
|
+
(e: React.KeyboardEvent) => {
|
|
253
|
+
if (e.key === "ArrowDown") {
|
|
254
|
+
e.preventDefault()
|
|
255
|
+
setActiveIndex((i) => Math.min(i + 1, flatItems.length - 1))
|
|
256
|
+
} else if (e.key === "ArrowUp") {
|
|
257
|
+
e.preventDefault()
|
|
258
|
+
setActiveIndex((i) => Math.max(i - 1, 0))
|
|
259
|
+
} else if (e.key === "Enter" && flatItems[activeIndex]) {
|
|
260
|
+
e.preventDefault()
|
|
261
|
+
onSelect?.(flatItems[activeIndex].key)
|
|
262
|
+
}
|
|
263
|
+
},
|
|
264
|
+
[flatItems, activeIndex, onSelect]
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
// Reset active index on search change
|
|
268
|
+
React.useEffect(() => setActiveIndex(0), [search])
|
|
269
|
+
|
|
270
|
+
return (
|
|
271
|
+
<div
|
|
272
|
+
ref={listRef}
|
|
273
|
+
className={cn(
|
|
274
|
+
"w-72 max-h-80 overflow-y-auto",
|
|
275
|
+
"rounded-lg border bg-popover shadow-lg",
|
|
276
|
+
"p-1",
|
|
277
|
+
className
|
|
278
|
+
)}
|
|
279
|
+
role="listbox"
|
|
280
|
+
onKeyDown={handleKeyDown}
|
|
281
|
+
>
|
|
282
|
+
{/* Search input */}
|
|
283
|
+
<div className="px-2 pb-1.5">
|
|
284
|
+
<input
|
|
285
|
+
type="text"
|
|
286
|
+
value={search}
|
|
287
|
+
onChange={(e) => setSearch(e.target.value)}
|
|
288
|
+
placeholder="Rechercher..."
|
|
289
|
+
className={cn(
|
|
290
|
+
"w-full rounded-md border bg-background px-2 py-1.5",
|
|
291
|
+
"text-sm placeholder:text-muted-foreground",
|
|
292
|
+
"focus:outline-none focus:ring-1 focus:ring-ring"
|
|
293
|
+
)}
|
|
294
|
+
autoFocus
|
|
295
|
+
/>
|
|
296
|
+
</div>
|
|
297
|
+
|
|
298
|
+
{/* Commands */}
|
|
299
|
+
{flatItems.length === 0 && (
|
|
300
|
+
<div className="px-2 py-4 text-center text-sm text-muted-foreground">
|
|
301
|
+
Aucune commande trouvee
|
|
302
|
+
</div>
|
|
303
|
+
)}
|
|
304
|
+
|
|
305
|
+
{Array.from(groups.entries()).map(([groupName, cmds]) => (
|
|
306
|
+
<div key={groupName}>
|
|
307
|
+
<div className="px-2 py-1 text-xs font-semibold text-muted-foreground uppercase tracking-wider">
|
|
308
|
+
{groupName}
|
|
309
|
+
</div>
|
|
310
|
+
{cmds.map((cmd) => {
|
|
311
|
+
const globalIndex = flatItems.indexOf(cmd)
|
|
312
|
+
const Icon = cmd.icon
|
|
313
|
+
return (
|
|
314
|
+
<button
|
|
315
|
+
key={cmd.key}
|
|
316
|
+
role="option"
|
|
317
|
+
aria-selected={globalIndex === activeIndex}
|
|
318
|
+
className={cn(
|
|
319
|
+
"flex w-full items-center gap-3 rounded-md px-2 py-1.5 text-left",
|
|
320
|
+
"text-sm transition-colors cursor-pointer",
|
|
321
|
+
globalIndex === activeIndex
|
|
322
|
+
? "bg-accent text-accent-foreground"
|
|
323
|
+
: "hover:bg-accent/50"
|
|
324
|
+
)}
|
|
325
|
+
onMouseEnter={() => setActiveIndex(globalIndex)}
|
|
326
|
+
onClick={() => onSelect?.(cmd.key)}
|
|
327
|
+
>
|
|
328
|
+
<div className="flex h-8 w-8 shrink-0 items-center justify-center rounded-md border bg-background">
|
|
329
|
+
<Icon className="h-4 w-4" />
|
|
330
|
+
</div>
|
|
331
|
+
<div className="min-w-0 flex-1">
|
|
332
|
+
<div className="font-medium">{cmd.label}</div>
|
|
333
|
+
{cmd.description && (
|
|
334
|
+
<div className="text-xs text-muted-foreground truncate">
|
|
335
|
+
{cmd.description}
|
|
336
|
+
</div>
|
|
337
|
+
)}
|
|
338
|
+
</div>
|
|
339
|
+
</button>
|
|
340
|
+
)
|
|
341
|
+
})}
|
|
342
|
+
</div>
|
|
343
|
+
))}
|
|
344
|
+
</div>
|
|
345
|
+
)
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
WakaSlashMenu.displayName = "WakaSlashMenu"
|
|
349
|
+
|
|
350
|
+
// ─── Plugin helper ───────────────────────────────────────────────────────────
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Creates slash command plugin configuration for Plate.
|
|
354
|
+
* Call this function and spread the result into your plugins array.
|
|
355
|
+
*
|
|
356
|
+
* @example
|
|
357
|
+
* ```ts
|
|
358
|
+
* import { createSlashCommandPlugins } from "@wakastellar/ui/editor"
|
|
359
|
+
*
|
|
360
|
+
* const editor = usePlateEditor({
|
|
361
|
+
* plugins: [
|
|
362
|
+
* ...otherPlugins,
|
|
363
|
+
* ...await createSlashCommandPlugins(),
|
|
364
|
+
* ],
|
|
365
|
+
* })
|
|
366
|
+
* ```
|
|
367
|
+
*/
|
|
368
|
+
export async function createSlashCommandPlugins(
|
|
369
|
+
commands: SlashCommand[] = DEFAULT_SLASH_COMMANDS
|
|
370
|
+
) {
|
|
371
|
+
try {
|
|
372
|
+
const { SlashPlugin, SlashInputPlugin } = await import("@platejs/combobox/react")
|
|
373
|
+
return [
|
|
374
|
+
SlashPlugin.configure({
|
|
375
|
+
options: {
|
|
376
|
+
trigger: "/",
|
|
377
|
+
triggerPreviousCharPattern: /^$|^[\s"']$/,
|
|
378
|
+
rules: commands.map((cmd) => ({
|
|
379
|
+
key: cmd.key,
|
|
380
|
+
text: cmd.label,
|
|
381
|
+
keywords: cmd.keywords,
|
|
382
|
+
onSelect: cmd.action,
|
|
383
|
+
})),
|
|
384
|
+
},
|
|
385
|
+
}),
|
|
386
|
+
SlashInputPlugin,
|
|
387
|
+
]
|
|
388
|
+
} catch {
|
|
389
|
+
console.warn("[WakaSlashMenu] @platejs/combobox not installed, slash commands disabled")
|
|
390
|
+
return []
|
|
391
|
+
}
|
|
392
|
+
}
|